react 源码解析 7.Fiber 架构
视频解说(高效学习):进入学习
往期文章:
1. 开篇介绍和面试题
2.react 的设计理念
3.react 源码架构
4. 源码目录构造和调试
5.jsx& 外围 api
6.legacy 和 concurrent 模式入口函数
7.Fiber 架构
8.render 阶段
9.diff 算法
10.commit 阶段
11. 生命周期
12. 状态更新流程
13.hooks 源码
14. 手写 hooks
15.scheduler&Lane
16.concurrent 模式
17.context
18 事件零碎
19. 手写迷你版 react
20. 总结 & 第一章的面试题解答
Fiber 的深度了解
react15 在 render 阶段的 reconcile 是不可打断的,这会在进行大量节点的 reconcile 时可能产生卡顿,因为浏览器所有的工夫都交给了 js 执行,并且 js 的执行时单线程。为此 react16 之后就有了 scheduler 进行工夫片的调度,给每个 task(工作单元)肯定的工夫,如果在这个工夫内没执行完,也要交出执行权给浏览器进行绘制和重排,所以异步可中断的更新须要肯定的数据结构在内存中来保留工作单元的信息,这个数据结构就是 Fiber。
那么有了 Fiber 这种数据结构后,能实现哪些事件呢,
- 工作单元 工作合成 :Fiber 最重要的性能就是作为工作单元,保留原生节点或者组件节点对应信息(包含优先级),这些节点通过指针的形似造成 Fiber 树
- 增量渲染 :通过 jsx 对象和 current Fiber 的比照,生成最小的差别补丁,利用到实在节点上
- 依据优先级暂停、持续、排列优先级 :Fiber 节点上保留了优先级,能通过不同节点优先级的比照,达到工作的暂停、持续、排列优先级等能力,也为下层实现批量更新、Suspense 提供了根底
- 保留状态: 因为 Fiber 能保留状态和更新的信息,所以就能实现函数组件的状态更新,也就是 hooks
Fiber 的数据结构
Fiber 的自带的属性如下:
//ReactFiber.old.js
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// 作为动态的数据结构 保留节点的信息
this.tag = tag;// 对应组件的类型
this.key = key;//key 属性
this.elementType = null;// 元素类型
this.type = null;//func 或者 class
this.stateNode = null;// 实在 dom 节点
// 作为 fiber 数架构 连接成 fiber 树
this.return = null;// 指向父节点
this.child = null;// 指向 child
this.sibling = null;// 指向兄弟节点
this.index = 0;
this.ref = null;
// 用作为工作单元 来计算 state
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
//effect 相干
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
// 优先级相干的属性
this.lanes = NoLanes;
this.childLanes = NoLanes;
//current 和 workInProgress 的指针
this.alternate = null;
}
Fiber 双缓存
当初咱们晓得了 Fiber 能够保留实在的 dom,实在 dom 对应在内存中的 Fiber 节点会造成 Fiber 树,这颗 Fiber 树在 react 中叫 current Fiber,也就是以后 dom 树对应的 Fiber 树,而正在构建 Fiber 树叫 workInProgress Fiber,这两颗树的节点通过 alternate 相连.
function App() {
return (
<>
<h1>
<p>count</p> xiaochen
</h1>
</>
)
}
ReactDOM.render(<App />, document.getElementById("root"));
构建 workInProgress Fiber 产生在 createWorkInProgress 中,它能创立或者服用 Fiber
//ReactFiber.old.js
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
let workInProgress = current.alternate;
if (workInProgress === null) {// 辨别是在 mount 时还是在 update 时
workInProgress = createFiber(
current.tag,
pendingProps,
current.key,
current.mode,
);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps;// 复用属性
workInProgress.type = current.type;
workInProgress.flags = NoFlags;
workInProgress.nextEffect = null;
workInProgress.firstEffect = null;
workInProgress.lastEffect = null;
//...
}
workInProgress.childLanes = current.childLanes;// 复用属性
workInProgress.lanes = current.lanes;
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
const currentDependencies = current.dependencies;
workInProgress.dependencies =
currentDependencies === null
? null
: {
lanes: currentDependencies.lanes,
firstContext: currentDependencies.firstContext,
};
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
return workInProgress;
}
- 在 mount 时:会创立 fiberRoot 和 rootFiber,而后依据 jsx 对象创立 Fiber 节点,节点连接成 current Fiber 树。
-
在 update 时:会依据新的状态造成的 jsx(ClassComponent 的 render 或者 FuncComponent 的返回值)和 current Fiber 比照形(diff 算法)成一颗叫 workInProgress 的 Fiber 树,而后将 fiberRoot 的 current 指向 workInProgress 树,此时 workInProgress 就变成了 current Fiber。fiberRoot:指整个利用的根节点,只存在一个
fiberRoot:指整个利用的根节点,只存在一个
rootFiber:ReactDOM.render 或者 ReactDOM.unstable_createRoot 创立进去的利用的节点,能够存在多个。
咱们当初晓得了存在 current Fiber 和 workInProgress Fiber 两颗 Fiber 树,Fiber 双缓存指的就是,在通过 reconcile(diff)造成了新的 workInProgress Fiber 而后将 workInProgress Fiber 切换成 current Fiber 利用到实在 dom 中,存在双 Fiber 的益处是在内存中造成视图的形容,在最初利用到 dom 中,缩小了对 dom 的操作。
当初来看看 Fiber 双缓存创立的过程图 :
-
mount 时:
- 刚开始只创立了 fiberRoot 和 rootFiber 两个节点
- 而后依据 jsx 创立 workInProgress Fiber:
- 把 workInProgress Fiber 切换成 current Fiber
- 刚开始只创立了 fiberRoot 和 rootFiber 两个节点
-
update 时
- 依据 current Fiber 创立 workInProgress Fiber
- 把 workInProgress Fiber 切换成 current Fiber
- 依据 current Fiber 创立 workInProgress Fiber