Virtual DOM 与 Fiber

- Published on

Virtual DOM 与 Fiber
Fiber 是践行代数效应的产物。
Fiber 是 React16 中新增的协调引擎.
fiber 是一个对象,指Fiber 节点
(Fiber Node 实例)包含了组件相关的信息,和组件实例有对应关系,通过 stateNode 跟踪实例。
一个 fiber node:
{
child, stateNode, siblings, alternate, return, type, key
}
1. Virtual DOM(虚拟 DOM)
1.1 什么是 Virtual DOM
2. 为何需要 Fiber
React 的使命是"快速构建用户界面"。
React 15 之前,组件的更新是同步的,这样可能会阻塞浏览器的渲染,影响体验。
所以,React 团队想要组件可以异步更新,且必须满足:
- 1. 更新可以暂停和继续
- 2. 更新要有优先级,高优先级可以打断低优先级。
用户操作一般具有更高优先级。
JS 中实现代码暂停的是 generator
.
generator 可以完成第一条,但是无法实现第二条。
而且,generator 有传染性,想使用 generator 的话别的函数必须使用它的语法。
浏览器
有个获取浏览器渲染间歇的 幕后任务协作调度 API,它就是 requestIdleCallback,可惜的是它的兼容性是个问题?
基于以上两点,React 团队开发出了一个新的架构 Fiber。
2. Fiber 是什么
Fiber 这个词不是 React 团队创造的。
Fiber(纤程) 和系统中的进程、线程,以及 generator 的 协程是不同的。
维基百科中的定义:在计算机科学中,纤程(英语:Fiber)是一种最轻量化的线程(lightweight threads)。它是一种用户态线程(user thread),让应用程序可以独立决定自己的线程要如何运作。操作系统内核不能看见它,也不会为它进行调度。
2.1 指 Fiber 架构(Fiber Reconciler)
React 中的 Fiber 是指 Fiber 架构。
Fiber 架构就是 Fiber Reconciler,它是一套协调计算组件更新变化的方法。
2.2 指 fiberNode(Fiber Reconciler)
fiber 是指数据结构。
此时,一个 fiberNode 对应一个组件。
fiberNode 中保存了组件的数据。
此时的 fiberNode 就是我们所说的 虚拟 DOM。
例如:
function App() {
const [num, addNum] = useState(0);
return <p>hello{num}</p>;
}
ReactDOM.render(<App />, document.getElementById("root"));
我们调用 ReactDOM.render, React 会创建应用唯一的根节点:FiberRootNode;
每调用一次 ReactDOM.render, React 会创建一个 RootFiber,并将其挂载到 FiberRootNode 上;
然后会创建一个函数组件 App 的 fiberNode;
接着会创建 p 元素和文本节点 hello。
父节点通过 child 属性连接子节点;
子节点通过 return 属性连接父节点;
为何用 return,而不用 parent 呢?是不是有点奇怪?
因为 React 中对 fiberNode 的遍历是深度优先递归的,递的阶段先从父节点遍历到子节点,归的阶段子节点返回父节点,所以用 return。
fiberNode 关系图:

如果有一个 React 项目,且它的根节点 id 是“root”,我们可以通过如下命令看到 FiberRootNode。
document.querySelector("#root")._reactRootContainer;
FiberRootNode:

2.3 指 fiber 动态工作单元
我们来看一个具体的 fiberNode(RootFiber):
RootFiber:

3. Fiber 工作机制 -- 双缓存
机智的你可能发现,上面的容器根 fiberNode(RootFiber)上还有一个tag = 3
的 fiberNode。
RootFiber 的 alternate:

alternate 的意思是“交替”,每个 fiberNode 上都有这个属性,两个 Fiber 节点通过它来共享一些属性。
我想你已经明白了,它就是双缓存中用于交替 RootFiber 的 fiberNode。
首屏渲染时的 Fiber 逻辑:
- React 创建 FiberRootNode 这个 Fiber 根;
- React 创建 容器的根 Fiber 节点(RootFiber),FiberRootNode 的 current 指向它,它叫 CurrentFiber;
- 因为首屏渲染页面没有元素,所以 RootFiber 下没有子节点;
- 开始首屏渲染,再创建一个根 Fiber 节点(RootFiber),两个 RootFiber 通过 alternate 属性连接;
- 接着,根据组件内容深度优先创建一个新的 Fiber 树,叫 workProgressFiber
- 然后渲染新的 Fiber 树(workProgressFiber)
- 新的 Fiber 树渲染后,FiberRootNode 会改变 current 指向新的 RootFiber, workProgressFiber 就变成了新的 CurrentFiber;
- 这样就完成了一次交替
首屏渲染 fiber 树:

假如,我们通过点击按钮触发了组件更新,会新建一棵 Fiber 树(新的 workProgressFiber),新的 workProgressFiber 会基于 CurrentFiber 创建。
当前 CurrentFiber 的中的所有 fiberNode 的 alternate 属性指向上一个 CurrentFiber 的相应的 FiberNode,
更新组件渲染 fiber 树:

**将 CurrentFiber 和更新所生成的 React Element(一般根据 JSX 生成)做对比,生成新的 workProgressFiber 的算法就是 diff 算法。**🌟🌟🌟
链接:
Inside Fiber: an in-depth overview of the new reconciliation algorithm in React
react 核心团队成员文章:React Fiber Architecture
总结:
- fiber 是
- fiber 原理是
- fiber 优势
参考
这可能是最通俗的 React Fiber(时间分片) 打开方式
详细论述了基于 JSON 的 VirtualDOM:Your benefits of working with JSON based virtual DOM
解释了 Virtual DOM 真正的优点是让开发者更方便运用 DOM,而不是速度The Real Benefits of the Virtual DOM in React.js