参考文章:React技术揭秘——render阶段
简要回顾
render阶段开始于performSyncWorkOnRoot
或performConcurrentWorkOnRoot
方法的调用。这取决于本次更新是同步更新还是异步更新。
这两个方法的主要区别如下:
// performSyncWorkOnRoot会调用该方法
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
// performConcurrentWorkOnRoot会调用该方法
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
可以看到,他们唯一的区别是是否调用shouldYield。如果当前浏览器帧没有剩余时间,shouldYield会中止循环,直到浏览器有空闲时间后再继续遍历。这就是开启Concurrent模式后,原本的同步更新,通过时间切片(每帧时,留出一点固定时间给JS)变为可中断的异步更新。
我们要记住,Fiber Reconciler
是从 Stack Reconciler
重构而来。所以本质上在渲染时,还是要"递归",只不过这个"递归"现在是通过遍历的方式实现可中断的"递归"。
同时performSyncWorkOnRoot
和 performConcurrentWorkOnRoot
的源码区别中也有一个很关键的对象:workInProgress,详情可以点击这里查看
“递归”流程概览
- “递”(为每个遍历到的 Fiber 节点调用 beginWork 方法):从rootFiber开始向下深度优先遍历。为遍历到的每个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树结构
child属性 表示子节点,return 属性表示父节点,sibling 属性表示兄弟节点
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会特殊处理。
由于 beginWork / completeWork 内容太多,我再另外记录。
网友评论