render
在react项目中我们经常会见到类似下面的代码
ReactDOM.render(<APP/>,document.getElementById('root'));
从这段代码中我们可以看出 React 应用会在容器root上渲染出一个组件 这也是React的入口代码。接下来就来详细剖析render的流程。
首先
render阶段开始于 performSyncWorkOnRoot 或者 performConcurrentWorkOnRoot方法的调用。这取决于本次更新是同步更新还是异步更新。 但目前我们不需要掌握这两种方法,只需要知道在这两个方法中会调用如下两个方法。
// performSyncWorkOnRoot会调用该方法
function workLoopSync( ) {
while(workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
// performConcurrentWorkOnRoot会调用该方法
function workLoopConcurrent( ) {
while(workInProgress !== null&& !shouldYield{
performUnitOfWork(workInProgress);
}
}
在这个代码块中可以看到,他们的区别是 是否调用shouldYield。如果当前浏览器帧没有剩余时间,shouldYield会中止循环,知道浏览器有空闲时间后再继续遍历。
workInProgress 代表当前已创建的 workInProgress fiber 。
performUnitOfWork 方法会创建下一个 Fiber节点 并赋值给 workInProgress ,并将 workInProgress 与已创建的 Fiber节点 连接起来构成 Fiber树。
在其中 会分别创建两个root,一个 root 叫做 FiberRoot,另一个 root 叫做 RootFiber,并且它们两者还是相互引用的。
对于 FiberRoot 对象来说,我们现在只需要了解两个属性,分别是 containerInfo 及 current。前者代表着容器信息,也就是我们的 document.querySelector('#root');后者指向 RootFiber
我们知道Fiber Reconciler 是从 Stack Reconciler 重构而来,通过遍历的方式实现可中断的递归,所以performUnitOfWork 的工作可以分为两部分:“递”和“归”。
“递”阶段
首先从 rootFiber 开始向下深度优先遍历。为遍历到的每个 Fiber节点 调用beginWork方法 。
该方法会根据传入的 Fiber节点 创建 子Fiber节点 ,并将这两个 Fiber节点 连接起来。
当遍历到叶子节点(即没有子组件的组件)时就会进入“归”阶段。
“归”阶段
在“归”阶段会调用completeWork处理Fiber节点。 当某个Fiber节点 执行完completeWork,如果存在 兄弟Fiber节点 (也就是 fiber.sibling!==null),会进入兄弟Fiber的“递”阶段。 如果不存在 兄弟Fiber节点,会进入父级Fiber的“归”阶段。
在此,借用网上的一张图 来 直观的理解一下
“递”和“归”阶段会交替进行知道“归”到rootFiber。到此,render阶段的工作就结束了。