React源码解析 之 Fiber的渲染(1)

我心飞翔 分类:javascript

React源码系列

  • React源码解析之 Fiber结构的创建
  • React源码解析 之 Fiber的渲染(1)

通过上一篇《React Fiber结构的创建》,我们得到fiberRoot的结构,这一篇,主要讲述如何将得到的fiberRoot渲染到视图。

如何生成workInProgress结构

上篇通过查看调用栈生成了了一个fiberRoot,从调用updateContainer函数为入口函数,开始执行解析fiberRoot结构。

updateContainer(children, fiberRoot, parentComponent, callback);
 

解释一下,此时的children为未实例化的ReactNodeListparentComponentReact.render中的container(div#root)

updateContainer处理以下几件事。

一、 获取当前优先级lane(车道)

  const current = container.current;//container - fiber root
  // 获取优先级车道lane
  const lane = requestUpdateLane(current);
 

此时lane为1。

react v17版本后,以lane为优先级,lane值越小,优先级越高,lane为二进制存储,一共31位,每个位为一个车道。点击这里查看源码。

export const TotalLanes = 31;

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

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

// 输入框的值
const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000000010;
export const InputContinuousLane: Lanes = /*            */ 0b0000000000000000000000000000100;

export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000000001000;
export const DefaultLane: Lanes = /*                    */ 0b0000000000000000000000000010000;

 

requestUpdateLane中在初始化渲染时,lane为1。

export function requestUpdateLane(fiber: Fiber): Lane {
  // Special cases
  const mode = fiber.mode;
  if ((mode & ConcurrentMode) === NoMode) {
    // 0b0000000000000000000000000000001;
    // 优先级为1车道为1
    return (SyncLane: Lane);
    // ....
 

在react源码中,有一个文件,单独存放了不同种类型的优先级的lane的二进制值。
关于lane,后续会有单独文章进行解释其含义,以及使用lane进行权限设计的应用,此时我们只需要知道这个地方获取了lane作为fiber的优先级。

二、创建context。

在初始化时,通过getContextForSubtree函数,判断是否存在parentComponent,此时parentComponet为root的父元素,此时为null。
否则返回parentContext

function getContextForSubtree(
  parentComponent: ?React$Component<any, any>,
): Object {
  if (!parentComponent) {
    return emptyContextObject;
  }

  const fiber = getInstance(parentComponent);
  const parentContext = findCurrentUnmaskedContext(fiber);

  if (fiber.tag === ClassComponent) {
    const Component = fiber.type;
    if (isLegacyContextProvider(Component)) {
      return processChildContext(fiber, Component, parentContext);
    }
  }

  return parentContext;
}
 

三、创建update

创建update对象用来进行排队更新enqueueUpdate

// 创建update对象
  const update = createUpdate(eventTime, lane);
  // ...
  export function createUpdate(eventTime: number, lane: Lane): Update<*> {
  const update: Update<*> = {
    eventTime,
    lane,
    tag: UpdateState,// tag为 0|1|2|3
    payload: null,
    callback: null,

    next: null,
  };
  return update;
}
// ...
update.payload = { element };
callback = callback === undefined ? null : callback;
update.callback = callback;
}
// 队列更新
enqueueUpdate(current, update, lane);
 

enqueueUpdate主要用来添加一个共享队列sharedQueue,该队列可以和workInProgressFiberRoot进行共享队列。

export function enqueueUpdate<State>(
  fiber: Fiber,//fiberRoot
  update: Update<State>,
  lane: Lane,
) {
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    // Only occurs if the fiber has been unmounted.
    return;
  }
  // 当前队列和缓存队列共享一个持久化队列
  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
  // 比较fiber lane和lane,相同时更新
  // render初始化时不执行
  if (isInterleavedUpdate(fiber, lane)) {
    const interleaved = sharedQueue.interleaved;//交错更新
    if (interleaved === null) {
      // 如果是第一次更新,创建双向链表
      update.next = update;
      //在当前渲染结束时,将显示此队列的交错更新
      //被转移到挂起队列。
      pushInterleavedQueue(sharedQueue);
    } else {
      // interleaved.next ->  update.next   update - interleaved.next;
      // interleaved.next = update
      // update.next = interleaved.next = update
      update.next = interleaved.next;
      interleaved.next = update;
    }
    sharedQueue.interleaved = update;
  } else {
    const pending = sharedQueue.pending;
    if (pending === null) {
      //这是第一次更新。创建单向链表。
      update.next = update;
    } else {
      // 定义双向列表
      update.next = pending.next;
      pending.next = update;
    }
    sharedQueue.pending = update;
  }
}
 

四、调用scheduleUpdateOnFiber

处理Fiber schedule更新,在这里我们只看该次render函数会执行的逻辑,页面初始化时,此时只是先生成了fiberRoot,并未生成workInProgressRoot,此时workInProgressRootnull,因此,会走一下逻辑,调用performSyncWorkOnRoot

export function scheduleUpdateOnFiber(
  fiber: Fiber,
  lane: Lane,//当前更新lane
  eventTime: number,
): FiberRoot | null {
    ...
  //标记root有挂起的更新。
  markRootUpdated(root, lane, eventTime);
  if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
    ...
  }
    // root为fiber,workInfoProGressRoot为null,为false
  if (root === workInProgressRoot) {
   ...
  }

  if (lane === SyncLane) {
    if (
      // 检查我们是否在unbatchedUpdates
      (executionContext & LegacyUnbatchedContext) !== NoContext &&
      // 检查我们是否还没有渲染
      (executionContext & (RenderContext | CommitContext)) === NoContext
    ) {
      // 在根目录上注册挂起的交互,以避免丢失跟踪的交互数据。
      schedulePendingInteractions(root, lane);
      performSyncWorkOnRoot(root);
    } else {
      ensureRootIsScheduled(root, eventTime);
      schedulePendingInteractions(root, lane);
      if (
        executionContext === NoContext &&
        (fiber.mode & ConcurrentMode) === NoMode
      ) {
      //重置
        resetRenderTimer();
        flushSyncCallbackQueue();
      }
    }
  } else {
    //...
    ensureRootIsScheduled(root, eventTime);
    schedulePendingInteractions(root, lane);
  }

  return root;
}
 

performSyncWorkOnRoot会刷新Effects和同步渲染Fiber

//...
  if (
    root === workInProgressRoot &&
    areLanesExpired(root, workInProgressRootRenderLanes)
  ) {
    lanes = workInProgressRootRenderLanes;
    exitStatus = renderRootSync(root, lanes);
  } else {
    lanes = getNextLanes(root, NoLanes);
    // 同步渲染root
    exitStatus = renderRootSync(root, lanes);
  }
 //...
 

renderRootSync函数,首先会创建一个workInProgress的双向链表树,此树通过alternate和fiberRoot进行关联

...
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
    // 创建workInProgress的tree
    prepareFreshStack(root, lanes);
    // 开始处理挂起的交互,并将有交互的,绑定到store中即root.memoizedInteractions中
    startWorkOnPendingInteractions(root, lanes);
  }
 ...
 //创建workInProgress和workInProgressRoot。
 function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
  root.finishedWork = null;
  root.finishedLanes = NoLanes;

  const timeoutHandle = root.timeoutHandle;
  if (timeoutHandle !== noTimeout) {
    // The root previous suspended and scheduled a timeout to commit a fallback
    // state. Now that we have additional work, cancel the timeout.
    root.timeoutHandle = noTimeout;
    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
    cancelTimeout(timeoutHandle);
  }

  if (workInProgress !== null) {
    let interruptedWork = workInProgress.return;
    while (interruptedWork !== null) {
      unwindInterruptedWork(interruptedWork, workInProgressRootRenderLanes);
      interruptedWork = interruptedWork.return;
    }
  }
  workInProgressRoot = root;
  workInProgress = createWorkInProgress(root.current, null);//根据当前节点创建workInProgess
  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
  workInProgressRootExitStatus = RootIncomplete;
  workInProgressRootFatalError = null;
  workInProgressRootSkippedLanes = NoLanes;
  workInProgressRootUpdatedLanes = NoLanes;
  workInProgressRootPingedLanes = NoLanes;
  
    ... 
 }
 

执行后续代码,调用workLoopSync函数,开始处理workInProgress双向链表,进入beginWork阶段。

  do {
    try {
      workLoopSync();//同步更新
      break;
    } catch (thrownValue) {
      handleError(root, thrownValue);
    }
  } while (true);
  
  
  ...
  ...
  
  function workLoopSync() {
      // Already timed out, so perform work without checking if we need to yield.
      while (workInProgress !== null) {
        performUnitOfWork(workInProgress);//执行工作单元
      }
}
···
 

此时workInProgress结构已经生成,它的结构和左侧结构一致,我这边省略画了,通过中间的alternate进行连接。
图如下:

image.png

函数调用栈如下:

image.png

下一篇:beginWork阶段。

回复

我来回复
  • 暂无回复内容