React原理:通俗易懂的 Fiber
前言
React 16 之后,推出了 Fiber 架构,而 Fiber 具体是个什么东西,有很多优秀的文章进行解读,而本文在学习了各个大佬的文章后,尽力以最通俗易懂过的话来解释 Fiber。
React 16以前的老架构
在讲 Fiber 前,需要认识 React 16 前的架构
两层架构
React 16前的架构有两层:
Reconciler(协调器)
:对比新旧 VDOM 找不同Renderer(渲染器)
:将最新的 VDOM 转 真实DOM 渲染到页面上。
流程
当组件更新时,会调用 Reconciler(协调器):
- JSX 转 VDOM
- 新旧 VDOM 作比较
(老架构是 递归遍历 VDOM,不能中断)
- 递归结束,通知 Renderer(渲染器),将最新的 VDOM 渲染到页面上
缺点
React 16前,采用的是 递归遍历新旧 VDOM 树做对比
,这会存在一个问题:递归时,如果 VDOM树层级很深,那么会长时间占用 JS 主线程,而 JS又是单线程的,且递归又是同步递归的
,就会导致页面上的某些交互操作无法响应、动画卡顿等问题。所以为了解决这个问题,React 16后,新增了 Fiber 架构。
Fiber 架构
前面也说了,Fiber 架构的出现就是为了解决 数据量过大导致页面掉帧卡顿、不能及时响应操作的问题的
,那么 Fiber 具体是个什么东西呢?
Fiber 的含义
- 架构层面:
异步可中断
的方案,称为 Fiber Reconciler。React 16前没有 Fiber,递归调用栈,叫做 Stack Reconciler。 - 数据结构层面:保存了组件状态、信息的特殊
链表
,其中比较重要的三个属性:return(指向父节点)、child(指向子节点)、sibling(指向兄弟节点)
Fiber 工作流程
上面我们提到,React 16前的 Stack Reconciler 的工作流程是:
- Stack Reconciler 递归对比虚拟DOM找不同
- Renderer 接到 Reconciler 的通知,渲染新的 VDOM。
而 Fiber 架构里面变为了三个步骤:
- Scheduler(调度器):
设置任务的优先级
,优先级高的推入 Reconciler。 - Reconciler:VDOM 转 Fiber,对变化的 Fiber 节点打上标记 flag。对可复用的节点打上修改的标记,剩余的旧节点打上删除标记,新节点打上新增标记。也就是在协调器这一步,
就已经清楚了节点是增还是改还是删
。 - Renderer:根据 Reconciler 打的标记,直接渲染(因为上一步已经知道 Fiber 节点是如何变化的,所以渲染的时候就会很快)
在 Fiber 架构下,更新的顺序变成了:
- Scheduler 给每个更新任务
赋予优先级
- 优先级高的更新任务A,会被推入 Reconciler,VDOM 转 Fiber,
新的 VDOM 和 旧的 Fiber 进行 diff 对比
决定怎样生成新的 Fiber 。但如果此时有新的更高优先级的更新任务B
进入 Scheduler,那么A 就会被中断
,B被推入 Reconciler
,当 B 完成渲染后。新一轮的调度开始,A 是新一轮中优先级最高的,那 A 就继续推入 Reconciler 执行更新任务。 - 重复以上的
可中断、可重复
步骤,直至所有更新任务完成渲染。
流程图如下:
Fiber 数据结构
前面说了, Fiber 是个保存了组件状态、信息的特殊链表,其中三个属性 return、child、sibling 构成了链表的指向。
function App() {
return (
<div>
<p></p>
<a></a>
</div>
)
}
变成 Fiber 链表后:
Fiber 双缓存
React 更新DOM 采用的是双缓存技术。React 中最多会存在两颗 Fiber树:
currentFiber
:页面中显示的内容workInProgressFiber
:内存中正在重新构建的 Fiber树。
双缓存中:当 workInProgressFiber 在内存中构建完成后,React 会直接用它 替换掉 currentFiber,这样能快速更新 DOM。一旦 workInProgressFiber树 渲染在页面上后,它就会变成 currentFiber 树,也就是说 fiberRootNode 会指向它。
在 currentFiber 中有一个属性 alternate 指向它对应的 workInProgressFiber
,同样,workInProgressFiber 也有一个属性 alternate 指向它对应的 currentFiber
。也就是下面的这种结构:
Fiber 的构建与更新
接下来通过图解,说明 Fiber 的 Mount(构建) 和 Update(更新)
Mount
- 创建 fiberRoot 和 rootFiberNode 两个节点,
(fiberRoot是整个项目的根节点,只存在一个,rootFiber是应用的根节点,可能存在多个,例如多个 ReactDOM.render(<App />, document.getElementById("root"));创建多个应用节点。)
并根据 JSX 在内存中构建 workInProgressFiber 树。
左侧是 currentFiber,右侧是 workInProgressFiber。
workInProgressFiber 树构建完成后,将其变为 currentFiber树。
Update
- 根据 currentFiber 在内存中构建 workInProgressFiber。
构建 workInProgressFiber 是在createWorkInProgress中,它能创建或者服用Fiber
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;
}
- 构建完后,将 workInProgressFiber 替换成新的 currentFiber。
也就是说:jsx 改变,生成新的 VDOM, 用它和 旧的 Fiber(current Fiber)对比(diff算法),然后标记差异(是增加、修改、还是删除),形成一颗叫 workInProgress 的 Fiber树,然后将 fiberRoot 的 current 指向workInProgress树,此时 workInProgress 就变成了current Fiber
结语
以上内容如有错误,欢迎留言指出,一起进步💪,也欢迎大家一起讨论
参考文章
转载自:https://juejin.cn/post/7229552637229498405