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树
。
你可以从这里看到
workLoopConcurrent
的源码
我们知道Fiber Reconciler
是从Stack Reconciler
重构而来,通过遍历的方式实现可中断的递归,所以performUnitOfWork
的工作可以分为两部分:“递”和“归”。
“递”阶段
首先从rootFiber
开始向下深度优先遍历。为遍历到的每个Fiber节点
调用beginWork方法。
该方法会根据传入的Fiber节点
创建子Fiber节点
,并将这两个Fiber节点
连接起来。
当遍历到叶子节点(即没有子组件的组件)时就会进入“归”阶段。
“归”阶段
在“归”阶段会调用completeWork处理Fiber节点
。
当某个Fiber节点
执行完completeWork
,如果其存在兄弟Fiber节点
(即fiber.sibling !== null
),会进入其兄弟Fiber
的“递”阶段。
如果不存在兄弟Fiber
,会进入父级Fiber
的“归”阶段。
“递”和“归”阶段会交错执行直到“归”到rootFiber
。至此,render阶段
的工作就结束了。
例子
以上一节的例子举例:
function App() {
return (
<div>
i am
<span>KaSong</span>
</div>
)
}
ReactDOM.render(<App />, document.getElementById("root"));
对应的Fiber树
结构:
render阶段
会依次执行:
1. rootFiber beginWork
2. App Fiber beginWork
3. div Fiber beginWork
4. "i am" Fiber beginWork
5. "i am" Fiber completeWork
6. span Fiber beginWork
7. span Fiber completeWork
8. div Fiber completeWork
9. App Fiber completeWork
10. rootFiber completeWork
注意
之所以没有 “KaSong” Fiber 的 beginWork/completeWork,是因为作为一种性能优化手段,针对只有单一文本子节点的
Fiber
,React
会特殊处理。
总结
本节我们介绍了render阶段
会调用的方法。在接下来两节中,我们会讲解beginWork
和completeWork
做的具体工作。
参考资料
下一节:上一节我们了解到render阶段的工作可以分为“递”阶段和“归”阶段。其中“递”阶段会执行beginWork,“归”阶段会执行completeWork。这一节我们看看“递”阶段的beginWork方法究竟做了什么。