React 源码解析,构建fiber到输出 DOM树流程。

1,135 阅读9分钟

简介

  • react版本v17.3
  • 只写了函数,类,原生组件。
    • SuspenseComponent OffscreenComponent ...等组件没写。
  • 代码全部简化
    • 只留核心逻辑。
    • **DIFF **reconcileChildren 不讲。知道会产生新fiber即可。

工作流程

简单说,就是深度优先遍历(dfs)jsx树,生成fiber树。 react将 递归 流程转为while循环。

递归转while循环

workLoopSync、workLoopConcurrent

  • while循环 workInProgress 执行performUnitOfWork
    • 每次更新 workInProgress 为 新 fiber
  • performUnitOfWork
    • beginWork    
  • completeUnitOfWork
    • completeWork    
// 同步模式
function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}
// ConcurrentMode
function workLoopConcurrent() {
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}

递 performUnitOfWork

  • 开始工作 => 创建工作单元 fiber
    • 主要工作: childrenJSX => childrenFiber
      • 为什么每次工作都是处理 children ?
        1. diff 需要对新旧 children 进行比对。
        2. 构建 fibersibling return 等信息。
        3. dfs 递归“”下去。每次更新 workInProgress = childFber
  • 最终生成一棵 newFiberTree
// dfs
function performUnitOfWork(unitOfWork: Fiber): void {
  const current = unitOfWork.alternate;
  // 递 
  let next = beginWork(current, unitOfWork, subtreeRenderLanes);
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    // 归
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }
}

归 completeUnitOfWork

  • 完成工作 => fiber 树构建 宿主 树,并渲染。
    • 主要工作: fiber => instance 。 创建宿主实体
      • “归”是如何构建 宿主 树?
        • 每次将 childrenFiberinstance 添加到 workInProgressinstance 中。
      • dfs 递归“”上去,怎么实现?
        • fiber 完成 completeWork 后。
          • 新生成 workInProgress
          • 移动到 sibling
          • return
  • 最终渲染出 dom
function completeUnitOfWork(unitOfWork: Fiber): void {
  let completedWork = unitOfWork;
  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;
    let next = completeWork(current, completedWork, subtreeRenderLanes);
    if (next !== null) {
      workInProgress = next;
      return;
    }

    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      // If there is more work to do in this returnFiber, do that next.
      workInProgress = siblingFiber;
      return;
    }
    // Otherwise, return to the parent
    completedWork = returnFiber;
    // Update the next thing we're working on in case something throws.
    workInProgress = completedWork;
  } while (completedWork !== null);

  // We've reached the root.
  if (workInProgressRootExitStatus === RootIncomplete) {
    workInProgressRootExitStatus = RootCompleted;
  }
}

公共变量

  • didReceiveUpdate
    • 功能:false则 表明次fiber不需要update,true表明需要update。
    • 会修改此值的函数
      • beginWork
      • updateSimpleMemoComponent
      • markWorkInProgressReceivedUpdate()
        • 效果:didReceiveUpdate = true
        • 调用此函数者
          • renderWithHooks
          • renderWithReducer    
          • updateReducer
          • prepareToReadContext
      • bailoutOnAlreadyFinishedWork
  • workInProgress
    • 正在工作的fiber。

beginWork

通过 workInProgress.children (JSX) 创建 childrenFiber , 返回 childFiber ,第一个子fiber。

  • 为什么每次工作都是处理 children ?
    1. diff 需要对新旧 children 进行比对。
    2. 构建 fibersibling return 等信息。
    3. dfs 递归“”下去。每次更新 workInProgress = childFber
  • 逻辑:
    • 判断 workInProgress 是否需要 update
      • 判断是否存在 oldFiber ,
        • 存在 -- 判断新旧 props 是否不同,fiber 堆栈上下文 是否 改变
          • 是 props不同
            • 需要更新。didReceiveUpdate = false;
          • 否 props相同
            • 若 没有 current有被中断的更新。
              • 不需要更新。didReceiveUpdate = false;
              • return 克隆fiber,(其实就是切换alternate,继承oldFiber属性)
            • 判断 是否 有 forceUpdate 强制更新 flags
                • 需要更新。didReceiveUpdate = false;
                • 不需要更新。didReceiveUpdate = false;
        • 不存在 -- 说明是mount
          • 不需要更新。didReceiveUpdate = false;
    • workInProgress 任务lane 重置为 无任务
    • 根据workInProgress 工作类型 tag 进入不同工作函数
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
  if (current !== null) {
    // update,检测是否需要更新
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;
    if (
      oldProps !== newProps || // props不同 则更新 
      // Force a re-render if the implementation changed due to hot reload:
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      didReceiveUpdate = true;
    } else {
      // 通过检测lane context 是否需要更新
      const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
        current,
        renderLanes,
      );
      if (
        //                              无强制更新
        !hasScheduledUpdateOrContext && (workInProgress.flags & DidCapture) === NoFlags
      ) {
        didReceiveUpdate = false;
        // 直接克隆旧fiber
        return attemptEarlyBailoutIfNoScheduledUpdate(
          current,
          workInProgress,
          renderLanes,
        );
      }
      // Suspense是否有强制更新
      if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
        didReceiveUpdate = true;
      } else {
        didReceiveUpdate = false;
      }
    }
  } else {
    // mount阶段非更新
    didReceiveUpdate = false;
  }
	// 开始工作了,lane重置
  workInProgress.lanes = NoLanes;

  switch (workInProgress.tag) {
    // 函数组件mount时 tag为此。执行后 修改tag。 例如不继承Component的组件。
    case IndeterminateComponent: {
      return mountIndeterminateComponent(
        current,
        workInProgress,
        workInProgress.type,
        renderLanes,
      );
    }
    // 函数组件
    case FunctionComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateFunctionComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderLanes,
      );
    }
    // 类组件
    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,
        renderLanes,
      );
    }
    // 原生组件 div等
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderLanes);
    case HostText:
      return updateHostText(current, workInProgress);
}

不同工作函数 tag -> 工作函数

reconcileChildrendiff 下面会讲。

HostComponent -> updateHostComponent

简介

  • 处理原生组件,浏览器中为 DOM

功能

  • 生成 childrenFiber
  • 性能优化:子代是单文本节点,则子代不生成fiber。

逻辑

  • 子代是否为单文本节点
    • 是 单文本
      • 子代不生成 fiber 。nextChildren = null
    • 不是 单文本
      • oldFiber 是单文本
        • 标记内容清空 ContentReset
  • markRef 标记是否需要ref
  • reconcileChildren diff
  • return workInProgress.child
function updateHostComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  const type = workInProgress.type;
  const nextProps = workInProgress.pendingProps;
  const prevProps = current !== null ? current.memoizedProps : null;
  let nextChildren = nextProps.children;
  // 优化:子代是否为单文本节点  eg  <div>children只有一个文本节点</div>
  const isDirectTextChild = shouldSetTextContent(type, nextProps);
  if (isDirectTextChild) {
    // 文本节点不生成fiber,commit中直接渲染文本
    nextChildren = null;
  } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
    workInProgress.flags |= ContentReset;
  }
  markRef(current, workInProgress);
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}

HostText -> updateHostText

什么都不做,直接return null。

IndeterminateComponent -> mountIndeterminateComponent

简介

  • function 组件 mount 时,走此函数。历史遗留问题产物。

功能

  • 生成 childrenFiber
  • 兼容性:处理旧react支持的工厂( factory )函数即 Function 返回有 class 组件方法的对象。

逻辑

  • 当函数组件执行 renderWithHooks 获取到 value
  • 判断 value 是否为模拟factory class的对象。
    • factory class
      • tag 修正为 ClassComponent
      • 创建 class组件
      • return finishClassComponent
    • 否 是 函数组件
      • tag  修正为 FunctionComponent
      • reconcileChildren
      • return workInProgress.child

function mountIndeterminateComponent(
  _current,
  workInProgress,
  Component,
  renderLanes,
) {
  const props = workInProgress.pendingProps;
  let context;
  // 清空之前的context
  prepareToReadContext(workInProgress, renderLanes);
  // 运行组件函数
  value = renderWithHooks(
    null,
    workInProgress,
    Component,
    props,
    context,
    renderLanes,
  );
	// 判断函数是否返回一个 有render方法的类似clas组件的对象
  if (
    !disableModulePatternComponents &&
    typeof value === 'object' &&
    value !== null &&
    typeof value.render === 'function' &&
    value.$$typeof === undefined
  ) {
    // 按照ClassComponent 处理此fiber
    workInProgress.tag = ClassComponent;
    workInProgress.memoizedState = null;
    workInProgress.updateQueue = null;
    let hasContext = false;
    if (isLegacyContextProvider(Component)) {
      hasContext = true;
      pushLegacyContextProvider(workInProgress);
    } else {
      hasContext = false;
    }

    workInProgress.memoizedState =
      value.state !== null && value.state !== undefined ? value.state : null;
    initializeUpdateQueue(workInProgress);
    adoptClassInstance(workInProgress, value);
    mountClassInstance(workInProgress, Component, props, renderLanes);
    return finishClassComponent(
      null,
      workInProgress,
      Component,
      true,
      hasContext,
      renderLanes,
    );
  } else {
    // 按照FunctionComponent处理
    workInProgress.tag = FunctionComponent;
    // diff
    reconcileChildren(null, workInProgress, value, renderLanes);
    return workInProgress.child;
  }
}

FunctionComponent -> updateFunctionComponent

简介

  • function 组件 update 时,走此函数。
  • 构建hooks

功能

  • 生成 childrenFiber

逻辑

  • prepareToReadContext
  • renderWithHooks 获取到 nextChildren
  • current 存在且 didReceiveUpdateflase 则不需要更新。
    • bailoutHooks
    • return bailoutOnAlreadyFinishedWork
  • 需要更新
  • reconcileChildren
  • return workInProgress.child
function updateFunctionComponent(
  current,
  workInProgress,
  Component,
  nextProps: any,
  renderLanes,
) {
  let context;
  if (!disableLegacyContext) {
    const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
    context = getMaskedContext(workInProgress, unmaskedContext);
  }
	
  let nextChildren;
  // 清空之前的context
  prepareToReadContext(workInProgress, renderLanes);
  // 运行组件函数
  nextChildren = renderWithHooks(
    current,
    workInProgress,
    Component,
    nextProps,
    context,
     renderLanes,
  );
	// 是否需要更新
  if (current !== null && !didReceiveUpdate) {
    bailoutHooks(current, workInProgress, renderLanes);
    // 克隆旧fiber
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }
	// diff
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}

ClassComponent -> updateClassComponent

简介

  • class 组件走此函数。

功能

  • 处理生命周期。
  • 生成 childrenFiber

逻辑

  • prepareToReadContext-- 清空context
  • 若是 mount 。 fiber.stateNode === null; class组件的stateNode值为class 实例。
    • constructClassInstance
      • context 。 readContext
      • 执行构造函数 constructor
      • 赋值 state 。fiber.memoizedState = instance.state
    • mountClassInstance
      • fiber 初始化 updateQueue 。 initializeUpdateQueue
      • context 赋值到 instance.context
      • **生命周期    **
        • applyDerivedStateFromProps( getDerivedStateFromProps )
          • 更新 instance.statefiber.memoizedState 和 ``updateQueue.baseState
        • 若只有 componentWillMount ,无新LifecyclesgetDerivedStateFromPropsgetSnapshotBeforeUpdate
          • 执行callComponentWillMount
            • componentWillMount
            • classComponentUpdater.enqueueReplaceState
          • processUpdateQueue
          • 更新 instance.state
    • shouldUpdate = true;
  • 若是 update
    • shouldUpdate = updateClassInstance
      • 若无新 Lifecycles 则执行 componentWillReceiveProps  -- callComponentWillReceiveProps
        • 执行后,若 newState !== oldState
          • classComponentUpdater.enqueueReplaceState
            • enqueueUpdate
      • 批更新,计算 newState 。 -- processUpdateQueue()
        • 赋值 fiber.memoizedState    
        • 能处理优先级
      • **生命周期  **getDerivedStateFromProps()
      • 若需要更新
        • **生命周期  **componentWillUpdate()
      • 若需要更新且存在 componentDidUpdate() getSnapshotBeforeUpdate()
        • fibet.flags 写入 Update  / Snapshot
      • 更新 fibet.memoizedProps fiber.memoizedState
      • class实例更新,
        • 更新 instaceprops state context
      • return shouldUpdate;
  • finishClassComponent
    • 不需要更新
      • bailoutOnAlreadyFinishedWork
    • 需要更新
      • reconcileChildren
  • return workInProgress.child
function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
) {
  //清空context
  prepareToReadContext(workInProgress, renderLanes);
  const instance = workInProgress.stateNode;
  let shouldUpdate;
  if (instance === null) {
    if (current !== null) {
      current.alternate = null;
      workInProgress.alternate = null;
      workInProgress.flags |= Placement;
    }
    // 读取context,执行构造函数,赋值state
    constructClassInstance(workInProgress, Component, nextProps);
    // fiber初始化updateQueue,context赋值到实例
    mountClassInstance(workInProgress, Component, nextProps, renderLanes);
    shouldUpdate = true;
  } else if (current === null) {
    // 复用实例
    shouldUpdate = resumeMountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
  } else {
    // 计算state,执行 及将更新 相关的生命周期
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
  }
  // 就行检测 需要更新则diff   不需要更新则克隆
  const nextUnitOfWork = finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderLanes,
  );
  return nextUnitOfWork;
}

Fragment -> updateFragment

简介

  • 处理Fragment组件
    • pendingProps是childrenJSX

功能

  • 生成 childrenFiber

逻辑

  • reconcileChildren
  • return workInProgress.child
function updateFragment(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  const nextChildren = workInProgress.pendingProps;
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}

updateHostRoot

创建react组件root 功能:

  • Reconciler协调 rootFiber ,通过 reconcileChildrenjsx ,构建 children fiber 返回 child fiber

逻辑:

  • fiberRootstate.element 值为 jsx ,即 ReactDOM.render 中的第一个值。
  • 通过 state.elementreconcileChildren

CompleteWork

workInProgress 创建/更新 stateNode 。 将 childrenFiberstateNode 全部添加到 workInProgress.stateNode 中。 subtreeFlags, childLanes 会向上冒泡。 mount 时:创建dom,设置属性 update 时:diff props, 创建updateQueuecommit中处理.

  • 为什么要将所有childstateNode添加到workInProgress.stateNode中?
    • 最终root会形成dom树,方便直接插入dom树。
  • 逻辑:
    • 根据workInProgress 工作类型 tag 进入不同switch块
      • 主要工作是处理HostComponent
      • Class Function组件只向上冒泡flagslanebubbleProperties

不同工作函数 tag -> switch块

HostComponent

功能

  • 创建/更新 dom

逻辑

  • 若为更新。 current !== null && workInProgress.stateNode != null   
    • updateHostComponent
      • 新旧props不同则更新。
      • workInProgress.updateQueue = prepareUpdate
        • 进行dom属性的diff
        • updatePayload([key,value]数组,i为修改属性, i+1为修改的值) / null
    • 新旧ref不同,
      • markRef
  • 若为创建
    • createInstance
      • 创建dom
    • appendAllChildren
      • childrenFiberstateNode 全部添加到 workInProgress.stateNode
    • finalizeInitialChildren
      • 设置属性。
        • 处理子节点为单文本节点的情况。 setTextContent(domElement, nextProp)
    • markRef
  • bubbleProperties
  • return null
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
  updateHostComponent(
    current,
    workInProgress,
    type,
    newProps,
    rootContainerInstance,
  );

  if (current.ref !== workInProgress.ref) {
    markRef(workInProgress);
  }
} else {
  // mount dom插入
  const currentHostContext = getHostContext();
    const instance = createInstance(
      type,
      newProps,
      rootContainerInstance,
      currentHostContext,
      workInProgress,
    );
    appendAllChildren(instance, workInProgress, false, false);
    workInProgress.stateNode = instance;
    if (
      finalizeInitialChildren(instance,type,newProps,rootContainerInstance,currentHostContext,)
    ) {
      markUpdate(workInProgress);
    }
  if (workInProgress.ref !== null) {
    markRef(workInProgress);
  }
}
bubbleProperties(workInProgress);
return null;

HostText -> updateHostText

功能

  • 创建/更新 text节点

逻辑

  • 若为更新。 current !== null && workInProgress.stateNode != null   
    • updateHostText
      • 标记Update.
  • 若为创建
    • createTextInstance
      • 创建text节点
  • bubbleProperties
  • return null
const newText = newProps;
if (current && workInProgress.stateNode != null) {
  const oldText = current.memoizedProps;
  // flags写入 update 
  updateHostText(current, workInProgress, oldText, newText);
} else {
  const rootContainerInstance = getRootHostContainer();
  const currentHostContext = getHostContext();
  // 创建文本节点
  workInProgress.stateNode = createTextInstance(
    newText,
    rootContainerInstance,
    currentHostContext,
    workInProgress,
  );
}

bubbleProperties

功能:fiber属性subtreeFlags, childLanes冒泡 实现:

  • subtreeFlags 合并childrenflagssubtreeFlags
  • childLanes 合并childrenlanechildLane

每个fiber都有其子树的subtreeFlags, childLanes 

CommitRoot

fiber 树,渲染并且执行相关阶段工作。

  1. 处理 useEffectuesLayoutEffect
  2. 处理 classgetSnapshotBeforeUpdatecomponentDidMountcomponentDidUpdatecomponentWillUnmount
  3. 赋值 ref
  4. 依次执行(this.setState, ReactDOM.render) 的callback
  5. 修改视图DOM。PlacementUpdateDeletion

逻辑:

  • 思路:
    • 每个阶段都遍历 root.finishedWork
      • render 阶段完成构建的新fibet树。
      • 值其实是 root.current.alternate
    • 每次DFS fiber树,执行相关阶段work
    • 性能优化: subtreeFlags此阶段相关的 Flags  对比为 NoFlags  则跳过此子树

处理useEffect -- 注意!!! 调度,下一个宏任务执行

  • scheduleCallback( flushPassiveEffects )
    • 调度执行 useEffect 的destroy和create
    • 先destroy -- commitPassiveUnmountEffects
    • 在create -- commitPassiveMountEffects
scheduleCallback(NormalSchedulerPriority, () => {
  // 下个宏任务执行 处理useEffect 先执行destory,在create
  flushPassiveEffects();
  return null;
});

BeforMutation 阶段 -- 页面变动前 -- commitBeforeMutationEffects

  • 功能:
    • DFS fiber树,执行getSnapshotBeforeUpdate
  • 逻辑:
    • commitBeforeMutationEffects_begin
    • commitBeforeMutationEffects_complete
    • commitBeforeMutationEffectsOnFiber
      • 执行commitBeforeMutationEffects
      • 处理 Snapshot ,即 getSnapshotBeforeUpdate 。暂时无其他操作
  • **Mutation**** 阶段** -- 页面变动中 -- commitMutationEffects
    • 功能:    
      • DFS fiber树,处理 Delete  Plcament Update
    • 逻辑:
      • commitMutationEffects_begin
        • 先处理被删除的fiber
          • fiber.deletions 存在,则循环 Delete 每一项fiber
            • 注意:被删除的fiber,不在 finishedFiberTree 中,只在 returnFiber.deletions
            • commitDeletion
            • commitNestedUnmounts
              • FunctionComponent : layoutEffect destroy
              • ClassComponent: componentWillUnmount
              • HostConpment: 清空ref
            • detachFiberMutation
              • fiber,alternate return 置空
        • commitMutationEffects_complete
        • commitMutationEffectsOnFiber
          • 判断 flags 执行work
            • ContentReset

              • 清空文本
            • Ref

              • 清空ref. layout会重新赋值
            • Visibility

            • Plcament

              • commitPlacement
                • stateNode  插入 宿主
            • Update

              • commitWork
                • FunctionComponent:layoutEffect destroy
                • HostComponent: 更新属性
                  • commitUpdate updateQueue
                  • 处理Children是单文本节点的优化
                • HostText: 更新文本
function commitMutationEffects_begin(root: FiberRoot) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    const deletions = fiber.deletions;
    // deletions的fiber不存在finishedWork fiber树中,
    // 因为在diff时不会被复用
    if (deletions !== null) {
      for (let i = 0; i < deletions.length; i++) {
        const childToDelete = deletions[i];
        // dfs 马上删除的fiber
        // function : effect destroy
        // class: componentWillUnmount
        // host: 清除ref
        // 然后卸载stateNode
        commitDeletion(root, childToDelete, fiber);
      }
    }
    const child = fiber.child;
    // 性能优化: 若subtreeFlags无任务,则直接处理fiber,跳过子树,向上 "归",
    if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {
      nextEffect = child;
    } else {
      // 跳过无任务子树
      // 无任务子树 或则 叶子节点
      commitMutationEffects_complete(root);
    }
  }
}
function commitMutationEffects_complete(root: FiberRoot) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    commitMutationEffectsOnFiber(fiber, root);
    // 切换sibling
    const sibling = fiber.sibling;
    if (sibling !== null) {
      nextEffect = sibling;
      return;
    }
    // 切换 return
    nextEffect = fiber.return;
  }
}
function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
  const flags = finishedWork.flags;
  if (flags & ContentReset) {
    // 清空文本
    commitResetTextContent(finishedWork);
  }
  if (flags & Ref) {
    const current = finishedWork.alternate;
    // 清空ref
    if (current !== null) {
      commitDetachRef(current);
    }
  }
  // 新功能 先跳过 
  if (flags & Visibility) {}

  const primaryFlags = flags & (Placement | Update | Hydrating);
  outer: switch (primaryFlags) {
    case Placement: {
      // DOM插入 若有flags ContentReset则重置文本
      commitPlacement(finishedWork);
      finishedWork.flags &= ~Placement;
      break;
    }
    case PlacementAndUpdate: {
      commitPlacement(finishedWork);
      finishedWork.flags &= ~Placement;
      const current = finishedWork.alternate;
      // 更新
      // layoutEffect destroy
      // hostComponent 更新属性
      // hostText 更新文本
      commitWork(current, finishedWork);
      break;
    }
    case Update: {
      const current = finishedWork.alternate;
      commitWork(current, finishedWork);
      break;
    }
  }
}
  • root.current = finishedWork;
    • 变动完,切换树
    • 为什么在此处切换?
      • 在此之前,旧 fiber 树工作要依赖
        • eg:mutation 阶段中的 componentWillUnmount 是要依赖旧fiber树的,所以不切换
      • 之后 layout 阶段,eg: componentDidMountcomponentDidUpdate 是要依赖于新fiber树,所以切换fiber树

Layout 阶段 -- 页面变动后 -- commitLayoutEffects

  • 功能:
    • DFS fiber树,处理 componentDidMountcomponentDidUpdatelayoutEffect createref
      • 执行class的setState的callback
  • 逻辑:
    • commitLayoutEffects_begin
      • 处理 OffscreenComponent
    • commitLayoutMountEffects_complete
    • commitLayoutEffectOnFiber
      • commitHookEffectListMount
        • Function:layoutEffect create
        • ClasscomponentDidUpdate / componentDidMount
          • 判断curren 区分mount / update
          • 执行this.setState的callback -- commitUpdateQueue
        • HostComponent: Renderer -- commitMount
          • Renderer可能会需要执行任务
      • commitAttachRef
        • 更新ref
// 同相似Mutation的dfs
function commitLayoutEffectOnFiber(
  finishedRoot: FiberRoot,
  current: Fiber | null,
  finishedWork: Fiber,
  committedLanes: Lanes,
): void {
  if ((finishedWork.flags & LayoutMask) !== NoFlags) {
    switch (finishedWork.tag) {
      case FunctionComponent: {
        // layoutEffect create
        commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
        break;
      }
      case ClassComponent: {
        const instance = finishedWork.stateNode;
        if (finishedWork.flags & Update) {
            if (current === null) {
              // 生命周期 componentDidMount
              instance.componentDidMount();
            } else {
              const prevProps =
                finishedWork.elementType === finishedWork.type
                  ? current.memoizedProps
                  : resolveDefaultProps(
                    finishedWork.type,
                    current.memoizedProps,
                  );
              const prevState = current.memoizedState;
              // 生命周期componentDidUpdate
              instance.componentDidUpdate(
                  prevProps,
                  prevState,
                  instance.__reactInternalSnapshotBeforeUpdate,
                );
            }
        }
        const updateQueue = (finishedWork.updateQueue: any);
        if (updateQueue !== null) {
          // this.setState的callback 队列
          commitUpdateQueue(finishedWork, updateQueue, instance);
        }
        break;
      }
      case HostRoot: {
        const updateQueue: UpdateQueue<*,
          > | null = (finishedWork.updateQueue: any);
        if (updateQueue !== null) {
          let instance = null;
          if (finishedWork.child !== null) {
            switch (finishedWork.child.tag) {
              case HostComponent:
                instance = getPublicInstance(finishedWork.child.stateNode);
                break;
              case ClassComponent:
                instance = finishedWork.child.stateNode;
                break;
            }
          }
          commitUpdateQueue(finishedWork, updateQueue, instance);
        }
        break;
      }
      case HostComponent: {
        const instance: Instance = finishedWork.stateNode;
        if (current === null && finishedWork.flags & Update) {
          const type = finishedWork.type;
          const props = finishedWork.memoizedProps;
          // mount后执行,react-dom会执行focus
          commitMount(instance, type, props, finishedWork);
        }
        break;
      }
  }
  if (finishedWork.flags & Ref) {
    // 执行ref
    commitAttachRef(finishedWork);
  }
}
  • commit 阶段可能产生新的任务,进行处理。
    • ensureRootIsScheduled
    • flushSyncCallbacks

useEffect 与 useLayoutEffect

阶段useEffectuseLayoutEffect
beforeMutation
mutation执行destory(销毁函数)
layout执行create(useLayoutEffect)
下一个宏任务执行flushPassiveEffects,即依次执行destory,create

useLayoutEffect是同步执行 useEffect是commit后,异步执行