Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React16.2源码解析-视图改变 #15

Open
UNDERCOVERj opened this issue Jun 6, 2018 · 0 comments
Open

React16.2源码解析-视图改变 #15

UNDERCOVERj opened this issue Jun 6, 2018 · 0 comments

Comments

@UNDERCOVERj
Copy link
Owner

UNDERCOVERj commented Jun 6, 2018

继上一章批量更新优化,我们得到了一个 updateQueue ,且以 firstnext 形式将 update 对象链接起来。

updateQueue 挂载在具体的 instance 下,待遍历到该 instance 对应的 fiber 时,处理更新队列。

在回调函数执行完后,需要 performWork ,将状态的改变应用到视图上。这样就进入了页面渲染流程,不过不一样的是这次不是第一次渲染了。

  1. 当从 root 遍历到此 instance 对应的 fiber 时,会根据 updateQueue 处理得到新的 newState。处理过程是从第一个 update 对象开始,迭代循环 update.next,不停的使用 assign 形式,将 partialState 覆盖 oldStatenewState 上,直到不再有 nextUpdate,然后返回 newState。此过程中还能收集 setState 的回调函数,具体做法是将 callback 添加至 updateQueue.callbackList 队列,并设置 effectTag,以便在 commitRoot 阶段触发。具体做法如下图所示:

image

if (workInProgress.updateQueue !== null) {
    newState = processUpdateQueue(current, workInProgress, workInProgress.updateQueue, instance, newProps, renderExpirationTime);
}

workInProgress.effectTag |= Callback
  1. 在改变state之后,执行生命周期函数 shouldComponentUpdate, componentWillUpdate, render。在 render 执行之后,改变了 children,故须将改变了的差异应用到视图上。这时便要对 children 进行 reconcileChildren(调和)

  2. reconcileChildren 阶段将 nextChildren 和原先的 child 进行对比,可以选择重用原先的 childFiber ,并改变 pendingPropsmemoizedProps

  3. 之后,在 completeWork 的时候对新旧 props 进行 diff ,并将 workInProgress.updateQueue 置为最新 updatePayloadkey 为2n,value 为2n+1) ,并改变 workInProgress.effectTag |= Update

for (var i = 0; i < updatePayload.length; i += 2) {
    var propKey = updatePayload[i];
    var propValue = updatePayload[i + 1];
}
  1. 最后根据 workInProgress.effectTag 来决定执行 commit 操作

如何根据更新了的fiber来改变视图

commitAllHostEffects 阶段,将拿到更新后的 fiber - finishedWork 。在将 finishedWork 下面的 effect 串,按序应用,改变视图。

对每个 effectFibereffectTag 进行位运算处理,得到 primaryEffectTagprimaryEffectTag 代表三种操作:插入、更新、插入并更新、删除

var primaryEffectTag = effectTag & ~(Callback | Err | ContentReset | Ref | PerformedWork);
  1. 插入

利用 return 属性,找到 parentFiber

getHostSibling,是为了找到在哪个 fiber 对应的 dom 元素前插入目前 fiber 对应的 dom。这个元素是目前 fiber 之后 effectTag 不为 PlacementPlacementAndUpdatefiber 对应的 dom 元素

function commitPlacement () {
    var parentFiber = getHostParentFiber(finishedWork);
    var before = getHostSibling(finishedWork);
}
function getHostParentFiber(fiber) {
    var parent = fiber['return'];
    while (parent !== null) {
        if (isHostParent(parent)) {
            return parent;
        }
        parent = parent['return'];
    }
}

function getHostSibling(fiber) {
        var node = fiber;
        siblings: while (true) {
            while (node.sibling === null) {
                // 如果父级是HostComponent,返回null
                if (node['return'] === null || isHostParent(node['return'])) {
                    return null;
                }
                node = node['return'];
            }
            node.sibling['return'] = node['return'];
            node = node.sibling; // 直接看fiber.sibling
            while (node.tag !== HostComponent && node.tag !== HostText) {
                if (node.effectTag & Placement) {
                    continue siblings;
                }
                if (node.child === null || node.tag === HostPortal) {
                    continue siblings;
                } else {
                    node.child['return'] = node;
                    node = node.child;
                }
            }
            // 如果fiber的effectTag为Placement或者PlacementAndUpdate
            if (!(node.effectTag & Placement)) {
                return node.stateNode;
            }
        }
    }

在得到 parentFiberbefore 后,就能实行具体的节点插入措施,分别为 insertBeforeappendChild 措施

  • 更新

更新操作发生在 commitWork 中,由于更新操作在 diffProperties 算法中讲过,当重用 dom ,但是某些属性发生改变时需要更新,更新的内容以键值对形式 pushupdatePayload ,并挂载 fiber.updateQueue 上,得到 updatePayload,接着就需要 commitUpdate

function commitUpdate () {
    updateFiberProps(); // 将新的props挂载到 `dom` 下
    updateProperties();
}

image

updateDOMProperties$1 函数中更新 dom

style 的设置用驼峰命名,但 html 中的 style 属性用'-'分割

获取元素CSS值之getComputedStyle方法熟悉

  • 插入更新

先插入再更新

  • 删除

删除操作发生在 commitDeletion 中,卸载 dom 元素下面子组件,待组件卸载完后,删除 dom 节点,分离删除 fiber

@UNDERCOVERj UNDERCOVERj changed the title React16.2最新源码-视图改变 React16.2源码解析-视图改变 Jun 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant