react - beginWork

271 阅读2分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

beginWork

beginWork的工作是传入当前Fiber节点,创建子Fiber节点

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes
): Fiber | null {

  // update时:如果current存在可能存在优化路径,可以复用current(即上一次更新的Fiber节点)
if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;

    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() ||
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      didReceiveUpdate = true;
    } else if (!includesSomeLane(renderLanes, updateLanes)) {
      didReceiveUpdate = false;
      switch (workInProgress.tag) {
        // 省略处理
      }
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderLanes,
      );
    } else {
      didReceiveUpdate = false;
    }
  } else {
    didReceiveUpdate = false;
  }
  // mount时:根据tag不同,创建不同的子Fiber节点
  switch (workInProgress.tag) {
    case IndeterminateComponent: 
      // ...省略
    case LazyComponent: 
      // ...省略
    case FunctionComponent: 
      // ...省略
    case ClassComponent: 
      // ...省略
    case HostRoot:
      // ...省略
    case HostComponent:
      // ...省略
    case HostText:
      // ...省略
    // ...省略其他类型
  }
}

其中参数

  • current:当前组件对应的Fiber节点在上一次更新时的Fiber节点,即workInProgress.alternate
  • workInProgress:当前组件对应的Fiber节点
  • renderLanes:优先级相关,在讲解Scheduler时再讲解。

除rootFiber以外, 组件mount时,由于是首次渲染,是不存在当前组件对应的Fiber节点在上一次更新时的Fiber节点,即mountcurrent === null。组件update时,由于之前已经mount过,所以current !== null

所以我们可以通过current === null ?来区分组件是处于mount还是update

  • update时:如果current存在,在满足一定条件时可以复用current节点,这样就能克隆current.child作为workInProgress.child,而不需要新建workInProgress.child

  • mount时:除fiberRootNode以外,current === null。会根据fiber.tag不同,创建不同类型的子Fiber节点

mount

当不满足优化路径时,我们就进入第二部分,新建子Fiber

我们可以看到,根据fiber.tag不同,进入不同类型Fiber的创建逻辑。

update

满足如下情况时didReceiveUpdate === false(即可以直接复用前一次更新的子Fiber,不需要新建子Fiber

  1. oldProps === newProps && workInProgress.type === current.type,即propsfiber.type不变
  2. !includesSomeLane(renderLanes, updateLanes),即当前Fiber节点优先级不够,会在讲解Scheduler时介绍. 对于我们常见的组件类型,如(FunctionComponent/ClassComponent/HostComponent),最终会进入reconcileChildren方法。

effectTag

render阶段的工作是在内存中进行,当工作结束后会通知Renderer需要执行的DOM操作。要执行DOM操作的具体类型就保存在fiber.effectTag中。

通过二进制表示effectTag,可以方便的使用位操作为fiber.effectTag赋值多个effect

你可以从这里 (opens new window)看到effectTag对应的DOM操作

// DOM需要插入到页面中
export const Placement = /*                */ 0b00000000000010;
// DOM需要更新
export const Update = /*                   */ 0b00000000000100;
// DOM需要插入到页面中并更新
export const PlacementAndUpdate = /*       */ 0b00000000000110;
// DOM需要删除
export const Deletion = /*                 */ 0b00000000001000;