ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

React 图解系列:fiber 树构造的基础准备(react-illustration-series 源码级解读)

React 图解系列:fiber 树构造的基础准备(react-illustration-series 源码级解读) 教程前端【免费下载链接】react-illustration-series图解react源码, 用大量配图的方式, 致力于将react原理表述清楚.项目地址https://gitcode.com/gh_mirrors/re/react-illustration-series点击查看免费下载在 React 运行时中fiber树构造是react-reconciler包的核心职责它接收来自输入阶段的更新请求在内存中把ReactElement转换为fiber树并在回溯阶段同步创建出对应的DOM对象。本文基于仓库 react-illustration-series 的 docs/main/fibertree-prepare.md系统梳理正式进入fiber树构造之前必须掌握的基础知识reconciler 的 4 阶段定位、ReactElement/Fiber/DOM三者的关系、构造循环使用的全局变量、executionContext执行上下文、双缓冲技术、车道模型Lane优先级体系以及栈帧管理。读完本文你将具备直接阅读fiber树构造源码初次创建与对比更新所需的全部概念储备。reconciler 运作流程中的定位fiber 树构造处于第 3 阶段在正式解读fiber树构造之前需要先回顾 reconciler 运作流程 的 4 个阶段该流程中的核心函数调用关系如下图所示源码集中在react-reconciler包的ReactFiberWorkLoop.old.js输入阶段衔接react-dom包承接fiber更新请求可以参考 React 应用的启动过程。入口函数是scheduleUpdateOnFiber。注册调度任务与调度中心scheduler包交互注册调度任务task等待任务回调可以参考 React 调度原理(scheduler)。核心函数是ensureRootIsScheduled。执行任务回调在内存中构造出fiber树和DOM对象也就是fiber 树构造的重点内容。核心函数是performSyncWorkOnRoot/performConcurrentWorkOnRoot。输出与渲染器react-dom交互渲染DOM节点。核心函数是commitRoot。fiber树构造处于上述第 3 个阶段可以通过不同的视角来理解它在React运行时中所处的位置从scheduler调度中心的角度来看它是任务队列taskQueue中的一个具体的任务回调task.callback即 React 工作循环 中所述任务调度循环消费的task的一部分。从 React 工作循环 的角度来看它属于fiber树构造循环workLoopSync/workLoopConcurrent循环过程是一个标准的深度优先遍历——但从仓库路径看应写作 深度优先遍历。由于fiber树构造源码量比较大本系列根据React运行的内存状态分为 2 种情况来说明初次创建在React应用首次启动时界面还没有渲染此时并不会进入对比过程相当于直接构造一棵全新的树详细过程见 fiber 树构造(初次创建)。对比更新React应用启动后界面已经渲染。如果再次发生更新创建新fiber之前需要和旧fiber进行对比。最后构造的 fiber 树有可能是全新的也可能是部分更新的。无论是初次创建还是对比更新基础概念都是通用的本节将介绍这些基础知识为正式进入fiber树构造做准备。ReactElement、Fiber、DOM 三者的关系在 React 应用中的高频对象 一文中已经介绍了ReactElement和Fiber对象的数据结构。这里梳理出ReactElement、Fiber、DOM这 3 种对象的关系ReactElement 对象所有采用jsx语法书写的节点都会被编译器转换最终以React.createElement(...)的方式创建出一个与之对应的ReactElement对象。开发者书写的每一段 JSX 都会变成这种轻量级的描述对象其数据结构与key/type/props等属性的含义参见 React 应用中的高频对象。Fiber 对象fiber对象是通过ReactElement对象进行创建的多个fiber对象构成了一棵fiber树。fiber树是构造DOM树的数据模型fiber树的任何改动最后都体现到DOM树。注意fiber树中的节点通过return父节点、child第一个子节点、sibling下一个兄弟节点三个指针相互关联与ReactElement通过props.children关联的方式不同。DOM 对象文档对象模型。DOM将文档解析为一个由节点和对象包含属性和方法的对象组成的结构集合也就是常说的DOM树。JavaScript可以访问和操作存储在 DOM 中的内容也就是操作DOM对象进而触发 UI 渲染。它们之间的关系反映了我们书写的 JSX 代码到 DOM 节点的转换过程如下图所示注意开发人员能够控制的是JSX也就是ReactElement对象。fiber树是通过ReactElement生成的如果脱离了ReactElementfiber树也无从谈起。所以是ReactElement树不是严格的树结构为了方便也称为树驱动fiber树。fiber树是DOM树的数据模型fiber树驱动DOM树。开发人员通过编程只能控制ReactElement树的结构ReactElement树驱动fiber树fiber树再驱动DOM树最后展现到页面上。所以fiber树的构造过程实际上就是ReactElement对象到fiber对象的转换过程。全局变量fiber 树构造循环的状态载体从 React 工作循环 的角度来看整个构造过程被包裹在fiber树构造循环中对应源码位于react-reconciler包的ReactFiberWorkLoop.old.js。在React运行时ReactFiberWorkLoop.js闭包中的全局变量会随着fiber树构造循环的进行而变化这些变量是该闭包的私有变量更用于控制 react 应用的执行过程。其中核心的全局变量如下对照ReactFiberWorkLoop.old.js中executionContext、workInProgressRoot、workInProgress等变量的定义// 当前React的执行栈(执行上下文) let executionContext: ExecutionContext NoContext; // 当前root节点 let workInProgressRoot: FiberRoot | null null; // 正在处理中的fiber节点 let workInProgress: Fiber | null null; // 正在渲染的车道(复数) let workInProgressRootRenderLanes: Lanes NoLanes; // 包含所有子节点的优先级, 是workInProgressRootRenderLanes的超集 // 大多数情况下: 在工作循环整体层面会使用workInProgressRootRenderLanes, 在begin/complete阶段层面会使用 subtreeRenderLanes let subtreeRenderLanes: Lanes NoLanes; // 一个栈结构: 专门存储当前节点的 subtreeRenderLanes const subtreeRenderLanesCursor: StackCursorLanes createCursor(NoLanes); // fiber构造完后, root节点的状态: completed, errored, suspended等 let workInProgressRootExitStatus: RootExitStatus RootIncomplete; // 重大错误 let workInProgressRootFatalError: mixed null; // 整个render期间所使用到的所有lanes let workInProgressRootIncludedLanes: Lanes NoLanes; // 在render期间被跳过(由于优先级不够)的lanes: 只包括未处理的updates, 不包括被复用的fiber节点 let workInProgressRootSkippedLanes: Lanes NoLanes; // 在render期间被修改过的lanes let workInProgressRootUpdatedLanes: Lanes NoLanes; // 防止无限循环和嵌套更新 const NESTED_UPDATE_LIMIT 50; let nestedUpdateCount: number 0; let rootWithNestedUpdates: FiberRoot | null null; const NESTED_PASSIVE_UPDATE_LIMIT 50; let nestedPassiveUpdateCount: number 0; // 发起更新的时间 let currentEventTime: number NoTimestamp; let currentEventWipLanes: Lanes NoLanes; let currentEventPendingLanes: Lanes NoLanes;在源码中大部分变量都带有英文注释读者可自行查阅此处只列举了fiber树构造循环中最核心的变量。从源码结构看可以将其按职责分为几组执行上下文executionContext用位掩码记录当前处于 render/commit 的哪个阶段。工作指针workInProgressRoot当前 root、workInProgress当前正在构造的 fiber 节点是双缓冲技术见下文的直接体现。优先级车道workInProgressRootRenderLanes、subtreeRenderLanes、workInProgressRootIncludedLanes、workInProgressRootSkippedLanes、workInProgressRootUpdatedLanes它们共同记录本次 render 的优先级状态。退出状态与防嵌套workInProgressRootExitStatus、workInProgressRootFatalError以及NESTED_UPDATE_LIMIT 50等防止无限循环和嵌套更新的计数变量。这些变量组合在一起构成了fiber树构造这一活动记录的完整状态任何一次可中断渲染的中断与恢复都依赖这组全局变量来还原进度。执行上下文executionContext在全局变量中有executionContext代表渲染期间的执行栈或叫做执行上下文它也是一个二进制表示的变量通过位运算进行操作位运算的基础知识可以参考 React 算法之位运算。在源码中一共定义了 7 种执行栈ReactFiberWorkLoop.old.js中实际还定义了RetryAfterError等核心 7 种如下type ExecutionContext number; export const NoContext /* */ 0b0000000; const BatchedContext /* */ 0b0000001; const EventContext /* */ 0b0000010; const DiscreteEventContext /* */ 0b0000100; const LegacyUnbatchedContext /* */ 0b0001000; const RenderContext /* */ 0b0010000; const CommitContext /* */ 0b0100000;上文回顾了reconciler 运作流程的 4 个阶段这 4 个阶段只是一个整体划分。如果具体到每一次更新是有差异的。比如说Legacy模式下的首次更新不会经过调度中心第 2 阶段而是直接进入fiber树构造第 3 阶段。事实上正是executionContext在操控reconciler 运作流程源码体现在 reconciler 运作流程 中介绍的scheduleUpdateOnFiber函数export function scheduleUpdateOnFiber( fiber: Fiber, lane: Lane, eventTime: number, ) { if (lane SyncLane) { // legacy或blocking模式 if ( (executionContext LegacyUnbatchedContext) ! NoContext (executionContext (RenderContext | CommitContext)) NoContext ) { performSyncWorkOnRoot(root); } else { // 后续的更新 // 进入第2阶段, 注册调度任务 ensureRootIsScheduled(root, eventTime); if (executionContext NoContext) { // 如果执行上下文为空, 会取消调度任务, 手动执行回调 // 进入第3阶段, 进行fiber树构造 flushSyncCallbackQueue(); } } } else { // concurrent模式 // 无论是否初次更新, 都正常进入第2阶段, 注册调度任务 ensureRootIsScheduled(root, eventTime); } }这里有两个值得注意的位运算判断技巧详见 React 算法之位运算(executionContext LegacyUnbatchedContext) ! NoContext判断当前上下文包含LegacyUnbatchedContext按位与结果非 0。(executionContext (RenderContext | CommitContext)) NoContext先通过按位或组合多个标志位再判断当前上下文不包含RenderContext或CommitContext。在 render 过程中每一个阶段都会改变executionContextrender 之前会设置executionContext | RenderContextcommit 之前会设置executionContext | CommitContext假设在render过程中再次发起更新如在UNSAFE_componentWillReceiveProps生命周期中调用setState则可通过executionContext来判断当前的render状态从而决定更新是立即同步执行还是注册为调度任务。双缓冲技术double buffering在全局变量中有workInProgress还有不少以workInProgress来命名的变量。workInProgress的应用实际上就是React的双缓冲技术double buffering。在上文我们梳理了ReactElement、Fiber、DOM三者的关系fiber树的构造过程就是把ReactElement转换成fiber树的过程。在这个过程中内存里会同时存在 2 棵fiber树其一代表当前界面的fiber树已经被展示出来挂载到fiberRoot.current上。如果是初次构造初始化渲染页面还没有渲染此时界面对应的 fiber 树为空fiberRoot.current null。其二正在构造的fiber树即将展示出来挂载到HostRootFiber.alternate上正在构造的节点称为workInProgress。当构造完成之后重新渲染页面最后切换fiberRoot.current workInProgress使得fiberRoot.current重新指向代表当前界面的fiber树。此处涉及到 2 个全局对象fiberRoot和HostRootFiber在 React 应用的启动过程 中有详细的说明fiberRoot是react-reconciler运行时的全局上下文HostRootFiber是 fiber 树的根节点tag HostRoot二者在应用启动时被创建并互相引用root.current HostRootFiberHostRootFiber.stateNode root。用图来表述double buffering的概念如下构造过程中fiberRoot.current指向当前界面对应的fiber树同时正在内存中构建一棵新的 fiber 树新树的节点通过alternate与旧树对应节点关联构造完成并渲染切换fiberRoot.current指针使其继续指向当前界面对应的fiber树原来代表界面的 fiber 树变成了内存中双缓冲的意义在于界面始终渲染一棵完整、一致的 fiber 树即使构造过程中被中断时间切片、可中断渲染当前页面展示的旧树也不会受到污染直到整棵新树构造完毕并一次性切换指针。优先级车道模型Lane的三方面应用在全局变量中有不少变量都以 Lanes 命名如workInProgressRootRenderLanes、subtreeRenderLanes其作用见上文注释它们都与优先级相关。在前文 React 中的优先级管理 中我们介绍了React中有 3 套优先级体系LanePriority、SchedulerPriority、ReactPriorityLevel并了解了它们之间的关联。现在fiber树构造过程中将要深入分析车道模型Lane的具体应用。在整个react-reconciler包中Lane的应用可以分为 3 个方面update 优先级update.lane在 React 应用中的高频对象 一文中介绍过update对象它是一个环形链表。对于单个update对象来讲update.lane代表它的优先级称之为update优先级。观察其构造函数位于react-reconciler包的ReactUpdateQueue.old.js其优先级是由外界传入export function createUpdate(eventTime: number, lane: Lane): Update* { const update: Update* { eventTime, lane, tag: UpdateState, payload: null, callback: null, next: null, }; return update; }update对象的字段含义在 React 应用中的高频对象 中有完整解释eventTime为发起 update 事件的时间、lane为 update 所属的优先级、tag表示 update 种类UpdateState/ReplaceState/ForceUpdate/CaptureUpdate、payload为真正需要更新的数据、callback为 commit 完成后会调用的回调、next指向环形链表中的下一个。在React体系中有 2 种情况会创建update对象应用初始化在react-reconciler包中的updateContainer函数里该函数在 React 应用的启动过程 中已经介绍是react-dom与react-reconciler的衔接点export function updateContainer( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?React$Componentany, any, callback: ?Function, ): Lane { const current container.current; const eventTime requestEventTime(); const lane requestUpdateLane(current); // 根据当前时间, 创建一个update优先级 const update createUpdate(eventTime, lane); // lane被用于创建update对象 update.payload { element }; enqueueUpdate(current, update); scheduleUpdateOnFiber(current, lane, eventTime); return lane; }发起组件更新假设在 class 组件中调用setStateclassComponentUpdater中的enqueueSetStateconst classComponentUpdater { isMounted, enqueueSetState(inst, payload, callback) { const fiber getInstance(inst); const eventTime requestEventTime(); // 根据当前时间, 创建一个update优先级 const lane requestUpdateLane(fiber); // lane被用于创建update对象 const update createUpdate(eventTime, lane); update.payload payload; enqueueUpdate(fiber, update); scheduleUpdateOnFiber(fiber, lane, eventTime); }, };可以看到无论是应用初始化或者发起组件更新创建update.lane的逻辑都是一样的都是根据当前时间创建一个 update 优先级。requestUpdateLane位于ReactFiberWorkLoop.old.js的核心逻辑export function requestUpdateLane(fiber: Fiber): Lane { // Special cases const mode fiber.mode; if ((mode BlockingMode) NoMode) { // legacy 模式 return (SyncLane: Lane); } else if ((mode ConcurrentMode) NoMode) { // blocking模式 return getCurrentPriorityLevel() ImmediateSchedulerPriority ? (SyncLane: Lane) : (SyncBatchedLane: Lane); } // concurrent模式 if (currentEventWipLanes NoLanes) { currentEventWipLanes workInProgressRootIncludedLanes; } const isTransition requestCurrentTransition() ! NoTransition; if (isTransition) { // 特殊情况, 处于suspense过程中 if (currentEventPendingLanes ! NoLanes) { currentEventPendingLanes mostRecentlyUpdatedRoot ! null ? mostRecentlyUpdatedRoot.pendingLanes : NoLanes; } return findTransitionLane(currentEventWipLanes, currentEventPendingLanes); } // 正常情况, 获取调度优先级 const schedulerPriority getCurrentPriorityLevel(); let lane; if ( (executionContext DiscreteEventContext) ! NoContext schedulerPriority UserBlockingSchedulerPriority ) { // executionContext 存在输入事件. 且调度优先级是用户阻塞性质 lane findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes); } else { // 调度优先级转换为车道模型 const schedulerLanePriority schedulerPriorityToLanePriority(schedulerPriority); lane findUpdateLane(schedulerLanePriority, currentEventWipLanes); } return lane; }可以看到requestUpdateLane的作用是返回一个合适的 update 优先级legacy 模式返回SyncLane。blocking 模式如果当前调度优先级是ImmediateSchedulerPriority则返回SyncLane否则返回SyncBatchedLane。concurrent 模式正常情况下根据当前的调度优先级SchedulerPriority来生成一个lane通过schedulerPriorityToLanePriority转换。特殊情况下处于 suspense 过程中会优先选择TransitionLanes通道中的空闲通道如果所有TransitionLanes通道都被占用就取最高优先级相关逻辑位于react-reconciler包的ReactFiberLane.js。这里需要说明fiber.mode属性继承自根节点HostRootFiber.mode由启动模式的RootTag决定所以requestUpdateLane中判断的 legacy / blocking / concurrent 模式本质上是判断整个应用以何种模式启动——这一前置知识可以回顾 React 应用的启动过程。最后通过scheduleUpdateOnFiber(current, lane, eventTime);函数把update.lane正式带入了输入阶段。scheduleUpdateOnFiber是输入阶段的必经函数在本系列的文章中已经多次提到此处以update.lane的视角分析export function scheduleUpdateOnFiber( fiber: Fiber, lane: Lane, eventTime: number, ) { if (lane SyncLane) { // legacy或blocking模式 if ( (executionContext LegacyUnbatchedContext) ! NoContext (executionContext (RenderContext | CommitContext)) NoContext ) { performSyncWorkOnRoot(root); } else { ensureRootIsScheduled(root, eventTime); // 注册回调任务 if (executionContext NoContext) { flushSyncCallbackQueue(); // 取消schedule调度 ,主动刷新回调队列, } } } else { // concurrent模式 ensureRootIsScheduled(root, eventTime); } }当lane SyncLane也就是 legacy 或 blocking 模式中注册完回调任务之后ensureRootIsScheduled(root, eventTime)如果执行上下文为空会取消 schedule 调度主动刷新回调队列flushSyncCallbackQueue()。这里包含了一个热点问题setState到底是同步还是异步的标准答案如果逻辑进入flushSyncCallbackQueueexecutionContext NoContext则会主动取消调度并刷新回调立即进入fiber树构造过程。当执行setState下一行代码时fiber树已经重新渲染了故setState体现为同步。正常情况下不会取消schedule调度。由于schedule调度是通过MessageChannel触发宏任务故体现为异步这一机制详见 React 调度原理(scheduler) 中requestHostCallback通过MessageChannel发消息触发回调的介绍。渲染优先级renderLanes这是一个全局概念每一次render之前首先要确定本次render的优先级。具体对应到源码如下performSyncWorkOnRoot与performConcurrentWorkOnRoot是 reconciler 运作流程 中任务回调的具体实现// ...省略无关代码 function performSyncWorkOnRoot(root) { let lanes; let exitStatus; // 获取本次render的优先级 lanes getNextLanes(root, lanes); exitStatus renderRootSync(root, lanes); } // ...省略无关代码 function performConcurrentWorkOnRoot(root) { // 获取本次render的优先级 let lanes getNextLanes( root, root workInProgressRoot ? workInProgressRootRenderLanes : NoLanes, ); if (lanes NoLanes) { return null; } let exitStatus renderRootConcurrent(root, lanes); }可以看到无论是Legacy还是Concurrent模式在正式render之前都会调用getNextLanes获取一个优先级位于react-reconciler包的ReactFiberLane.js// ...省略部分代码 export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { // 1. check是否有等待中的lanes const pendingLanes root.pendingLanes; if (pendingLanes NoLanes) { return_highestLanePriority NoLanePriority; return NoLanes; } let nextLanes NoLanes; let nextLanePriority NoLanePriority; const expiredLanes root.expiredLanes; const suspendedLanes root.suspendedLanes; const pingedLanes root.pingedLanes; // 2. check是否有已过期的lanes if (expiredLanes ! NoLanes) { nextLanes expiredLanes; nextLanePriority return_highestLanePriority SyncLanePriority; } else { const nonIdlePendingLanes pendingLanes NonIdleLanes; if (nonIdlePendingLanes ! NoLanes) { // 非Idle任务 ... } else { // Idle任务 ... } } if (nextLanes NoLanes) { return NoLanes; } return nextLanes; }getNextLanes会根据fiberRoot对象上的属性expiredLanes、suspendedLanes、pingedLanes等这些字段在 React 应用的启动过程 中创建的fiberRoot对象上维护确定出当前最紧急的lanes优先检查root.pendingLanes是否有等待中的 lanes如果没有则返回NoLanes。其次检查root.expiredLanes是否有已过期的 lanes如果有则直接采用过期车道优先级固定为SyncLanePriority。否则在nonIdlePendingLanespendingLanes NonIdleLanes即剔除 Idle 任务后的等待车道中挑选最紧急的任务。此处返回的lanes会作为全局渲染的优先级用于fiber树构造过程中。针对fiber对象或update对象只要它们的优先级如fiber.lanes和update.lane比渲染优先级低都将会被忽略。车道模型之所以能用按位与快速判断某个 lane 是否包含于渲染 lanes 集合中其位掩码优势可以对照 React 算法之位运算 与 React 中的优先级管理 中的伪代码对比理解。fiber 优先级fiber.lanes在 React 应用中的高频对象 一文中介绍过fiber对象的数据结构。其中有 2 个属性与优先级相关fiber.lanes代表本节点的优先级。fiber.childLanes代表子节点的优先级。从FiberNode的构造函数中可以看出fiber.lanes和fiber.childLanes的初始值都为NoLanes。在fiber树构造过程中使用全局的渲染优先级renderLanes和fiber.lanes判断fiber节点是否更新核心判断逻辑位于react-reconciler包的ReactFiberBeginWork.old.js的beginWork函数如果全局的渲染优先级renderLanes不包括fiber.lanes证明该fiber节点没有更新可以复用进入bailoutOnAlreadyFinishedWork的 bailout 逻辑直接跳过本节点及其子树。如果不能复用进入创建阶段。function beginWork( current: Fiber | null, workInProgress: Fiber, renderLanes: Lanes, ): Fiber | null { const updateLanes workInProgress.lanes; if (current ! null) { const oldProps current.memoizedProps; const newProps workInProgress.pendingProps; if ( oldProps ! newProps || hasLegacyContextChanged() || // Force a re-render if the implementation changed due to hot reload: (__DEV__ ? workInProgress.type ! current.type : false) ) { didReceiveUpdate true; } else if (!includesSomeLane(renderLanes, updateLanes)) { didReceiveUpdate false; // 本fiber节点的没有更新, 可以复用, 进入bailout逻辑 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } } // 不能复用, 创建新的fiber节点 workInProgress.lanes NoLanes; // 重置优先级为 NoLanes switch (workInProgress.tag) { case ClassComponent: { const Component workInProgress.type; const unresolvedProps workInProgress.pendingProps; const resolvedProps workInProgress.elementType Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps); return updateClassComponent( current, workInProgress, Component, resolvedProps, // 正常情况下渲染优先级会被用于fiber树的构造过程 renderLanes, ); } } }beginWork的这一可复用判断是对比更新场景下性能优化的关键只有oldProps ! newProps、hasLegacyContextChanged()或渲染优先级包含fiber.lanes时该 fiber 节点才会被重新处理否则整棵子树都可以直接复用。includesSomeLane(renderLanes, updateLanes)的实现同样是基于位运算(renderLanes updateLanes) ! NoLanes的判断。栈帧管理在React源码中每一次执行fiber树构造也就是调用performSyncWorkOnRoot或者performConcurrentWorkOnRoot函数的过程都需要一些全局变量来保存状态。在上文中已经介绍最核心的全局变量。如果从单个变量来看它们就是一个个的全局变量。如果将这些全局变量组合起来它们代表了当前fiber树构造的活动记录。通过这一组全局变量可以还原fiber树构造过程比如时间切片的实现过程可参考 React 调度原理 中关于时间切片与可中断渲染的说明fiber树构造过程被打断之后需要还原进度全靠这一组全局变量。所以每次fiber树构造是一个独立的过程需要独立的一组全局变量在React内部把这一个独立的过程封装为一个栈帧stack简单来说就是每次构造都需要独立的空间。对于栈帧的深入理解请读者自行参考其他资料。所以在进行fiber树构造之前如果不需要恢复上一次构造进度都会刷新栈帧源码在ReactFiberWorkLoop.old.js的prepareFreshStack函数function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { const prevExecutionContext executionContext; executionContext | RenderContext; const prevDispatcher pushDispatcher(); // 如果fiberRoot变动, 或者update.lane变动, 都会刷新栈帧, 丢弃上一次渲染进度 if (workInProgressRoot ! root || workInProgressRootRenderLanes ! lanes) { resetRenderTimer(); // 刷新栈帧 prepareFreshStack(root, lanes); startWorkOnPendingInteractions(root, lanes); } } /** 刷新栈帧: 重置 FiberRoot上的全局属性 和 fiber树构造循环过程中的全局变量 */ function prepareFreshStack(root: FiberRoot, lanes: Lanes) { // 重置FiberRoot对象上的属性 root.finishedWork null; root.finishedLanes NoLanes; const timeoutHandle root.timeoutHandle; if (timeoutHandle ! noTimeout) { root.timeoutHandle noTimeout; cancelTimeout(timeoutHandle); } if (workInProgress ! null) { let interruptedWork workInProgress.return; while (interruptedWork ! null) { unwindInterruptedWork(interruptedWork); interruptedWork interruptedWork.return; } } // 重置全局变量 workInProgressRoot root; workInProgress createWorkInProgress(root.current, null); // 给HostRootFiber对象创建一个alternate, 并将其设置成全局 workInProgress workInProgressRootRenderLanes subtreeRenderLanes workInProgressRootIncludedLanes lanes; workInProgressRootExitStatus RootIncomplete; workInProgressRootFatalError null; workInProgressRootSkippedLanes NoLanes; workInProgressRootUpdatedLanes NoLanes; workInProgressRootPingedLanes NoLanes; }prepareFreshStack的职责可以分为 3 部分重置FiberRoot对象上的属性清空finishedWork、finishedLanes并取消可能存在的超时任务timeoutHandle。清理上一次中断的工作现场如果workInProgress ! null说明存在被打断的构造进度则沿着workInProgress.return链向上执行unwindInterruptedWork清理工作。重置全局变量把workInProgressRoot指向本次的root并通过createWorkInProgress(root.current, null)为HostRootFiber创建alternate副本作为新的workInProgress同时把workInProgressRootRenderLanes、subtreeRenderLanes、workInProgressRootIncludedLanes统一设置为本次的渲染车道lanes。注意其中的createWorkInProgress(root.current, null)其参数root.current即HostRootFiber作用是给HostRootFiber创建一个alternate副本。workInProgress指针指向这个副本即workInProgress HostRootFiber.alternate在上文double buffering中分析过HostRootFiber.alternate是正在构造的fiber树的根节点。这一初始化动作在 fiber 树构造(初次创建) 中被完整跟踪对应其中的status-freshstack状态图。总结本节是fiber树构造的准备篇核心结论可以归纳为以下 4 点定位fiber树构造位于 reconciler 运作流程 的第 3 阶段执行任务回调从不同视角看它既是scheduler任务队列中一个具体的task.callback又是 React 工作循环 中的fiber树构造循环。数据模型ReactElement树驱动fiber树、fiber树驱动DOM树fiber树构造的本质就是把ReactElement对象转换为fiber对象。状态载体ReactFiberWorkLoop.js闭包中的全局变量executionContext、workInProgress、workInProgressRoot、各类Lanes变量等是构造过程的活动记录executionContext通过位掩码控制 reconciler 的流程分支workInProgress配合alternate实现了双缓冲技术车道模型update.lane、renderLanes、fiber.lanes从 3 个层面管理构造过程的优先级。栈帧管理每次fiber树构造都依赖一组独立的全局面量作为栈帧prepareFreshStack负责在不需要恢复上一次进度时刷新栈帧、为HostRootFiber创建alternate副本并重置全部构造状态。有了这些基础知识fiber树构造的具体实现过程会更加简单清晰——下一步即可进入 fiber 树构造(初次创建) 或对比更新章节结合beginWork、completeWork与副作用队列firstEffect/lastEffect逐步跟踪整棵 fiber 树的诞生过程。赞分享教程前端【免费下载链接】react-illustration-series图解react源码, 用大量配图的方式, 致力于将react原理表述清楚.项目地址https://gitcode.com/gh_mirrors/re/react-illustration-series点击查看免费下载相关推荐图解 React 源码系列react-illustration-series 原理学习路线与源码导读图解 React 源码系列react illustration series 原理学习路线与源码导读 本文以 react illustration serie教程前端React Fiber树构建过程react-illustration-series之首次渲染完整流程React Fiber树构建过程react illustration series之首次渲染完整流程 你是否曾好奇React应用首次启动时屏幕上的内容是如何教程前端图解 React 源码从 fiber 视角理解状态与副作用react-illustration-series 之状态管理篇图解 React 源码从 fiber 视角理解状态与副作用react illustration series 之状态管理篇 本文基于 react17.0教程前端上一篇Display Driver Uninstaller (DDU)三步彻底解决显卡驱动冲突的终极方案下一篇游戏模组管理终极解决方案XXMI Launcher让你的二次元游戏体验全面升级创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表