从首次渲染了解React Fiber架构

从首次渲染了解React Fiber架构

关键全局变量

FiberRootNode相关:

export type RootTag = 0 | 1 | 2;

export const LegacyRoot = 0;    
export const BlockingRoot = 1;
export const ConcurrentRoot = 2;
复制代码

Mode FiberRootNode属性tag的可能值

export type TypeOfMode = number;

export const NoMode = 0b00000;  // 如创建RootFiber时使用此mod
export const StrictMode = 0b00001;
// TODO: Remove BlockingMode and ConcurrentMode by reading from the root
// tag instead
export const BlockingMode = 0b00010;
export const ConcurrentMode = 0b00100;
export const ProfileMode = 0b01000; // 开发模式下创建RootFiber时使用此mod
export const DebugTracingMode = 0b10000;
复制代码

update与reconcile

// The root we're working on
let workInProgressRoot: FiberRoot | null = null;
// The fiber we're working on
let workInProgress: Fiber | null = null;
// The lanes we're rendering
let workInProgressRootRenderLanes: Lanes = NoLanes;
复制代码

优先级以及lane

// LanePriority越大优先级越高
export const SyncLanePriority: LanePriority = 15;
export const SyncBatchedLanePriority: LanePriority = 14;

const InputDiscreteHydrationLanePriority: LanePriority = 13;
export const InputDiscreteLanePriority: LanePriority = 12;

const InputContinuousHydrationLanePriority: LanePriority = 11;
export const InputContinuousLanePriority: LanePriority = 10;

const DefaultHydrationLanePriority: LanePriority = 9;
export const DefaultLanePriority: LanePriority = 8;

const TransitionHydrationPriority: LanePriority = 7;
export const TransitionPriority: LanePriority = 6;

const RetryLanePriority: LanePriority = 5;

const SelectiveHydrationLanePriority: LanePriority = 4;

const IdleHydrationLanePriority: LanePriority = 3;
const IdleLanePriority: LanePriority = 2;

const OffscreenLanePriority: LanePriority = 1;

export const NoLanePriority: LanePriority = 0;

// 对应LanePriority,Lane越小优先级越高,优先级使用二进制展示可以更直白的展示当前所拥有的权限,且更方便权限合并与提取
const TotalLanes = 31;

export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;

export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;

export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;

const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;

export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;

const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;

const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;

export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;

export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;

const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;

export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;

export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;

export const NoTimestamp = -1;

let currentUpdateLanePriority: LanePriority = NoLanePriority;
复制代码

state更新方式

export const UpdateState = 0;
export const ReplaceState = 1;
export const ForceUpdate = 2;
export const CaptureUpdate = 3;
复制代码

FiberNode tag属性的可能值

export type WorkTag =
  | 0
  | 1
  | 2
  | 3
  | 4
  | 5
  | 6
  | 7
  | 8
  | 9
  | 10
  | 11
  | 12
  | 13
  | 14
  | 15
  | 16
  | 17
  | 18
  | 19
  | 20
  | 21
  | 22
  | 23
  | 24;

export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const FundamentalComponent = 20;
export const ScopeComponent = 21;
export const Block = 22;
export const OffscreenComponent = 23;
export const LegacyHiddenComponent = 24;
复制代码

FiberNode

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber tree 链表
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  // 组件属性数据
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;  // 更新队列,队列内放着即将要发生的变更状态
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // Effects
  this.flags = NoFlags;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  this.alternate = null;  // FiberNode,双缓冲之一
}
复制代码

FiberRootNode

function FiberRootNode(containerInfo, tag, hydrate) {
  this.tag = tag;
  this.containerInfo = containerInfo; // DOM容器,把整个React元素渲染到这个DOM内部
  this.pendingChildren = null;
  this.current = null;  // (HostRoot)FiberNode,指向当前已经完成的Fiber Tree 的Root
  this.pingCache = null;
  this.finishedWork = null; // (HostRoot)FiberNode|null,指向当前已经完成准备工作即将真实渲染的Fiber Tree Root
  this.timeoutHandle = noTimeout;
  this.context = null;
  this.pendingContext = null;
  this.hydrate = hydrate;
  this.callbackNode = null;
  this.callbackPriority = NoLanePriority;
  this.eventTimes = createLaneMap(NoLanes); // 更新时间数组,31位的数组
  this.expirationTimes = createLaneMap(NoTimestamp);    // 过期时间数组,31位的数组
  this.pendingLanes = NoLanes;
  this.suspendedLanes = NoLanes;
  this.pingedLanes = NoLanes;
  this.expiredLanes = NoLanes;
  this.mutableReadLanes = NoLanes;
  this.finishedLanes = NoLanes;
  this.entangledLanes = NoLanes;
  this.entanglements = createLaneMap(NoLanes);  // 纠缠数组,31位的数组,如:MutableSource
}
复制代码

从组装数据到任务调度的主线方法调用

legacyRenderSubtreeIntoContainer => createRootFiber => FiberRootNode => initialUpdateQueue => updateContainer => createUpdate => scheduleUpdateOnFiber => renderRootSync => workLoopSync => performUnitOfWork => 
复制代码

创建 ReactRoot、FiberRoot、(HostRoot)FiberNode,建立他们与 DOMContainer 的关系

legacyRenderSubtreeIntoContainer

  • 根据container创建初始FiberRoot:root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate = false)
  • fiberRoot = root._internalRoot;
  • 拿到创建的FiberRoot开始处理相关 // Initial mount should not be batched
  • updateContainer开始为调度作准备;

创建FiberRoot createFiberRoot函数:

创建root: FiberRootNode

  • root.current赋值为createHostRootFiber(tag = NoMode)所创建的FiberNode(root.current.mode = NoMode,在开发环境下会被赋值为ProfileMode)称之为RootFiber;
  • root.current.stateNode 赋值为 root
  • 返回root,至此FiberRootNode创建成功;

createRootImpl方法中的markContainerAsRoot

将container加上上面创建的FiberNode

markContainerAsRoot(root.current, container)将conrainer添加属性[internalContainerInstanceKey] = root.current

requestEventTime获取当前执行事件的时间戳,全局变量

function requestEventTime() {
  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
    // We're inside React, so it's fine to read the actual time.
    return now();
  } // We're not inside React, so we may be in the middle of a browser event.


  if (currentEventTime !== NoTimestamp) {
    // Use the same start time for all updates until we enter React again.
    return currentEventTime;
  } // This is the first update since React yielded. Compute a new start time.


  currentEventTime = now();
  return currentEventTime;
}
复制代码

初始化(HostRoot)FiberNode的UpdateQueue

updateContainer开始调度相关的工作

  • 获取当前执行时间;
  • requestUpdateLane,根据FiberNode的mod值计算获取优先级;
  • createUpdate(eventTime, lane),根据入参初始化一个update返回,如下:
return update = {
    eventTime: eventTime,   // update事件的时间,当前时间戳
    lane: lane, // lane优先级相关, 1
    tag: UpdateState,   // 普通的更新方式
    payload: null,
    callback: null,
    next: null
  };
复制代码
  • update的内容设置为render时传入的element,后续会根据element来生成fiber tree如下:
update.payload = {
    element: element
};
复制代码
  • enqueueUpdate(container.current, update), 此处为第一个update的下一个更新指向当前update(update.next = update;)=> container.current.updateQueue.shared.pending指向update
updateQueue = {
    baseState: element, // 首次update赋值为当前需要执行任务的element
    firstBaseUpdate: null,  // 队列中的第一个`Update`
    lastBaseUpdate: null,   // 队列中的最后一个`Update`
    shared: {
      pending: null // 准备要执行的update任务
    },
    effects: null   // 副作用
}
复制代码
  • scheduleUpdateOnFiber(container.current, lane, eventTime);
  • markUpdateLaneFromFiberToRoot(fiber, lane),给fiber属性lanes赋值
  • markRootUpdated(root, updateLane, eventTime)
root.pendingLanes |= updateLane;    // 即将执行的lanes
const higherPriorityLanes = updateLane - 1; // 计算比当前lane更高优先级的lanes

 // 取消同等或较低优先级的更新。
 root.suspendedLanes &= higherPriorityLanes; // 0
 root.pingedLanes &= higherPriorityLanes;  // 0

 const index = laneToIndex(updateLane); // 根据updateLane获取index,0
 eventTimes[index] = eventTime;   // 更新触发更新的时间,第一位设置了时间值
复制代码
  • performSyncWorkOnRoot 开始执行同步任务在root, 处理更新
lanes = getNextLanes(root, NoLanes);;  // 根据root fiber挂载的updateQueue获取下个要执行任务的lanes
exitStatus = renderRootSync(root, lanes = 1); // 同步render root
复制代码
  • renderRootSync

/**
  全局变量赋值备用
  workInProgressRoot = root;
  workInProgress = createWorkInProgress(root.current, null); 给全局变量workInProgress赋值为当前fiber节点的副本
  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
  workInProgressRootExitStatus = RootIncomplete;
  workInProgressRootFatalError = null;
  workInProgressRootSkippedLanes = NoLanes;
  workInProgressRootUpdatedLanes = NoLanes;
  workInProgressRootPingedLanes = NoLanes;
*/
prepareFreshStack(root, lanes); // 准备新的任务栈相关
/*
    performUnitOfWork 方法循环调用 beginWork 找到处理对应组件的 hander 方法
    采用深度优先(先序优先)的方式遍历创建子节点,遇到同级节点下有多个子节点时,会为每
    个节点创建一个 sibling 属性指向下一个同级节点
    当遍历到某个分支的最深节点(没有子节点)时调用 completeUnitOfWork 方法
    completeUnitOfWork 方法判断如果有 sibling(下一个同级节点)则返回给 performUnitOfWork
    没有 sibling 则寻找 return(父节点),如果父节点有 sibling 继续返回给 performUnitOfWork
    最终 return 为 null,代表所有遍历都已完成
    从而实现深度优先遍历去创建整个 Fiber tree

  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
*/
workLoopSync();
复制代码
// 全局变量
export let current: Fiber | null = null;    // 当前正在执行任务的fiber
export let isRendering: boolean = false;    // 是否正在render

const valueStack: Array<any> = [];  // 

let fiberStack: Array<Fiber | null>; // FiberNode栈
复制代码

performUnitOfWork(unitOfWork: FiberNode)

var current = unitOfWork.alternate;
/*
 全局变量赋值
 current = fiber;
 isRendering = false;
 */
setCurrentFiber(unitOfWork);
next = beginWork(current, unitOfWork, subtreeRenderLanes);  // 拿到下一个update queue.next, 并且clone当前current.updateQueue到workInProgress,workInProgress是实际用来执行操作的Fiber tree,如此则不会影响current

// 主线执行方法调用
beginWork => updateHostRoot => processUpdateQueue => reconcileChildren => reconcileChildFibers => reconcileSingleElement => createFiberFromElement => completeUnitWork => completeWork => createInstance => createElement => finalizeInitialChildren
复制代码
  • updateHostRoot

    深度优先遍历并按照updateQueue处理update更新对应的fiber

    • processUpdateQueue 处理更新队列,包括state(目前只有element)、重新赋值第一个update、最后一个update、将update的指向改为update.next循环处理直到pendingQueue为空则处理完成;

    • reconcileChildren 协调子节点

      Fiber Reconciler 在执行过程中,会分为 2 个阶段。

      • 阶段一,通过element生成 Fiber 树,得出需要更新的节点信息。这一步是一个渐进的过程,可以被打断。
      • 阶段二,将需要更新的节点一次过批量更新,这个过程不能被打断。

      深度优先处理

    • reconcileSingleElement 协调单个子节点元素

      • 当key相同和对应类型符合时分fragment, protal和其他情况,进行复用,并删除剩余节点。 key符合, 当类型不匹配时,删除当前节点;

      • key和type不匹配时,删除当前fiber。 在新增时,分frament 和其他情况创建fiber。

    • createFiberFromElement 根据element创建新的fiber构建最终完整的携带所有更新信息fiber tree

  • completeUnitOfWork

判断如果有 sibling(下一个同级节点)则返回给 performUnitOfWork

没有 sibling 则寻找 return(父节点),如果父节点有 sibling 继续返回给 performUnitOfWork,从而实现深度优先遍历去创建整个 Fiber tree。

  • completeWork 通过调用createInstance => createElement => finalizeInitialChildren来创建真实dom并添加properties,

  • 最后commitRoot添加到container,即div#root完成整个初次渲染。

总结

  • 创建初始FiberRootNode
  • 创建初始rootFiber并初始化updateQueue
  • 根据element创建update并赋值给updateQueue
  • 根据updateQueue复制FiberRoot.current到全局变量workInProgress并进行fiber tree调协生成最新的fiber tree
  • 根据workInProgress循环创建最新dom树
  • 将dom树添加到container
// 整体主线方法调用
legacyRenderSubtreeIntoContainer => createRootFiber => FiberRootNode => initialUpdateQueue => updateContainer => createUpdate => scheduleUpdateOnFiber => renderRootSync => workLoopSync => performUnitOfWork => beginWork => updateHostRoot => processUpdateQueue => reconcileChildren => reconcileChildFibers => reconcileSingleElement => createFiberFromElement => completeUnitWork => completeWork => createInstance => createElement => finalizeInitialChildren => commitRoot
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享