手写 Fiber 架构的 React
AI 摘要从 JSX、虚拟 DOM 到任务调度,手写理解 React Fiber 的核心机制。
为了解决渲染阻塞的问题,React 团队提出了 Fiber 架构。Talk is cheap, show me the code。
JSX 转虚拟 Dom
先来看现有的业务代码,需要把 jsx 转换成虚拟 dom。
通过 babel 将 jsx 转换成 react/jsx 的调用,react/jsx 会通过相应的算法,将不同的对象构建成一个树的结构。
常规业务框架
拿到数据结构,如果不考虑 Fiber,对这个树进行 dfs 就可以写一个简单的声明式框架。再加上一些生命周期,一个 FineUI 就设计好了。如果在 createDomElement 里面加入 diff 算法,就是一个 Preact。如果再加依赖收集,就得到了一个 Vue。
Fiber
现在的 React 虽然完成了声明式 UI,但如果虚拟 Dom 太大了,就会阻塞交互。为了不阻塞交互,分成两步:
- 怎么把一个大的任务拆解成一个个小的任务
- 怎么一个个小的任务合并成一个个大的任务
一个 Fiber 就是一个最小的任务调度单元。
FiberTree 数据结构
需要把 VirtualDomTree 转换成 FiberTree,一个结点有三个指针:
- child:第一个孩子结点
- sibling:相邻的右兄弟结点
- return:父结点
使用深度优先完成这个算法。
Concurrency
注意这里用的是 concurrency,而不是 parallel。concurrency 的核心点在于怎么切片。使用 requestIdleCallback 完成这个目的。
即使 FiberTree 非常复杂(无限长),也能正确处理用户响应,同时自己也能同时更新。
Commit
Fiber 的拆分以及 diff 的对比称为 Reconcile 阶段,最终渲染到 Dom 上的阶段称为 Commit 阶段。Reconcile 阶段是可以被打断的,Commit 阶段是不可以被打断的。
总结
首先并不存在 VirtualDomTree 先生成,然后转换成 FiberTree 这一过程。在 Reconcile 阶段完成以后,VirtualDomTree 和 FiberTree 是同时生成的。FiberTree 是对 VirtualDomTree 的扩展。
从宏观角度来看,整个过程分为 Reconcile 阶段和 Commit 阶段。
React Fiber 并不存在什么魔法,根据 Run-to-completion。一个最终对业务侧有意义的启发是:在我们的 React 业务代码中,一个组件应该做更少的事情,而不是一个组件把整个页面全部渲染完成。