/xia栽ke:52xueit.com/569/
那么双缓存 Fiber 树是如何构建的? 在处理好优先级和标记后,就会进入到 render 阶段,此时仍在 Reconciler 中。取决于本次更新为同步还是异步,React 会调用 performSyncWorkOnRoot 或 performConcurrentWorkOnRoot,前者为同步更新,后者为异步更新: tsx复制代码// performSyncWorkOnRoot会调用该方法 function workLoopSync() { while (workInProgress !== null) { performUnitOfWork(workInProgress); } } // performConcurrentWorkOnRoot会调用该方法 function workLoopConcurrent() { while (workInProgress !== null && !shouldYield()) { performUnitOfWork(workInProgress); } } 唯一的区别就是是否调用 shouldYield 方法,如果当前浏览器帧没有剩余时间,shouldYield会中止循环,直到浏览器有空闲时间后再继续遍历。 workInProgress 就是当前已创建的 workInProgress fiber,performUnitOfWork 用于创建下一个 fiber 节点并赋值给 workInProgress,然后连接起来称为 Fiber 树。 在 React 16 架构中的 Fiber Reconciler 是从 Stack Reconciler 重构而来,通过遍历的方式实现可中断的递归,所以 performUnitOfWork 的工作可以分为两部分:“递”和“归”。 2. 递阶段(beginWork) 从 rootFiber 向下深度优先遍历,调用 beginWork 方法,将注入的 fiber 节点创建子 filer 节点,然后连接在一起,直到叶子节点(即没有子组件的组件)时进入“归”阶段。 tsx复制代码function beginWork( current: Fiber | null, workInProgress: Fiber, renderLanes: Lanes, ): Fiber | null { // ...省略函数体 } current:当前组件对应的 Fiber 节点在上一次更新时的 Fiber 节点,即workInProgress.alternate workInProgress:当前组件对应的 Fiber 节点 renderLanes:优先级相关 2.1 mount 时 代码细节方面,mount 时,会根据 fiber.tag 的不同创建不同子 Fiber 节点: tsx复制代码// mount时:根据tag不同,创建不同的子Fiber节点 switch (workInProgress.tag) { case IndeterminateComponent: // ...省略 case LazyComponent: // ...省略 case FunctionComponent: // ...省略 case ClassComponent: // ...省略 case HostRoot: // ...省略 case HostComponent: // ...省略 case HostText: // ...省略 // ...省略其他类型 } 2.2 update 时 如果是 update 时,如果 current 树不为空,那么可以复用之前的 current Fiber,比如克隆 current.child 为 workInProgress.child,这样就不需要重复创建: tsx复制代码// update时:如果current存在可能存在优化路径,可以复用current(即上一次更新的Fiber节点) if (current !== null) { // ...省略 // 复用current return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } else { didReceiveUpdate = false; } 2.3 reconcileChildren reconcileChildren 方法是 Reconciler 的核心部分: 对于 mount 的组件:创建子 Fiber 节点 对于 update 的组件:对比当前和上一次更新的 Fiber 节点(Diff 算法),然后生成新的 Fiber 节点。 tsx复制代码export function reconcileChildren( current: Fiber | null, workInProgress: Fiber, nextChildren: any, renderLanes: Lanes ) { if (current === null) { // 对于mount的组件 workInProgress.child = mountChildFibers( workInProgress, null, nextChildren, renderLanes ); } else { // 对于update的组件 workInProgress.child = reconcileChildFibers( workInProgress, current.child, nextChildren, renderLanes ); } } 它在 beginWork 执行后调用,返回值为一个新的 Fiber 节点,最终都会给到 workInProgress.child,并作为下一次 performUnitOfWork 执行时的 workInProgress 传参。 reconcileChildFibers 方法执行后会为节点添加 effectTag 属性。 fiber.effectTag 保存了需要在 Renderer 中执行的 DOM 操作: tsx复制代码// DOM需要插入到页面中 export const Placement = /* / 0b00000000000010; // DOM需要更新 export const Update = / / 0b00000000000100; // DOM需要插入到页面中并更新 export const PlacementAndUpdate = / / 0b00000000000110; // DOM需要删除 export const Deletion = / */ 0b00000000001000; 3. 归阶段(completeWork) 调用 completeWork 方法处理 fiber 节点。如果该节点存在兄弟节点,则进入它的“递”阶段;如果不存在就进入父节点的“归”阶段,这样交错进行直到 rootFiber。