React18.2源码解析之初始化阶段(中)

477 阅读5分钟

记录学习过程,如有错误欢迎指出

开始之前(必看)

React version 18.2.0

DEV代码可以忽略,下面正文我使用省略号就代表是dev相关的代码,包含hydrate字样的也请忽略,那是服务端渲染相关,望周知

我使用深度优先(🐶)的方式讲解:即遇到函数先进入函数,执行完函数后,又退回之前的函数.而不是在一个函数中讲完了再讲函数中执行的函数(希望能听懂我在说什么^v^)

因为使用了深度优先的方式讲解,耦合比较重,不建议跳着看

入口

//在上一篇文章中我们已经讲完了createRoot干了什么
const root = ReactDOM.createRoot(document.querySelector("#app"));
//现在开始进入render(),非render阶段
root.render(<App />);

render(未完)

export function render(
  element: React$Element<any>,// 这里就是传入的jsx就是React$Element,即<App>
  container: Container,
  callback: ?Function,
) {
    
    .....

  // 判断是否是有效的DOM元素
  
  // ok,这里先进入isValidContainerLegacy
  if (!isValidContainerLegacy(container)) {
    throw new Error('Target container is not a DOM element.');
  }

  .....

isValidContainerLegacy

export function isValidContainerLegacy(node: any): boolean {
  return !!(
    node &&
    // 即是Element | Doucment | Fragment | COMMENT 就是有效的容器节点
    (node.nodeType === ELEMENT_NODE ||
      node.nodeType === DOCUMENT_NODE ||
      node.nodeType === DOCUMENT_FRAGMENT_NODE ||
      (node.nodeType === COMMENT_NODE &&
        (node: any).nodeValue === ' react-mount-point-unstable '))
  );
}

render(完)

export function render(
  element: React$Element<any>,// 这里就是传入的jsx就是React$Element,即<App>
  container: Container,
  callback: ?Function,
) {
  
  .....
  
  // 渲染子树到容器
  // 进入legacyRenderSubtreeIntoContainer函数
  return legacyRenderSubtreeIntoContainer(
    null, 
    element,
    container,
    false, 
    callback,
  );
}

legacyRenderSubtreeIntoContainer(未完)

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>, //null
  children: ReactNodeList, //App
  container: Container, //容器
  forceHydrate: boolean, //服务器渲染相关 false
  callback: ?Function, //回调
) {
  .....

  // 此时_reactRootContainer还不存在!!!
  const maybeRoot = container._reactRootContainer;
  let root: FiberRoot;
  // _reactRootContainer不存在,进入if
  if (!maybeRoot) {
    /**
     * 初始化,
     * _reactRootContainer一定不存在,因为初始化的时候没有这个属性
     * 所以从DOM容器创建root
     */
    // 从dom容器创建root
    // 进入legacyCreateRootFromDOMContainer函数
    root = legacyCreateRootFromDOMContainer(
      container, //dom
      children, //App
      parentComponent, //null
      callback,
      forceHydrate, //false
    );
  }
  
  .......

legacyCreateRootFromDOMContainer

function legacyCreateRootFromDOMContainer(
  container: Container, //dom
  initialChildren: ReactNodeList, //App
  parentComponent: ?React$Component<any, any>, //父组件 null
  callback: ?Function,
  isHydrationContainer: boolean, //forceHydrate   false
): FiberRoot {

  .....
  
  else {
    // 清除所有兄弟接节点
    // 即先清除<div id="root">下的所有内容
    // 应该是不允许root节点下的原来内容,会用后面的React$Element填充
    let rootSibling;
    while ((rootSibling = container.lastChild)) {
      //removeChild清除
      container.removeChild(rootSibling);
    }

    // 获取root实例后,对root执行callback
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(root);
        originalCallback.call(instance);
      };
    }

    // 创建容器对象
    // 这个函数上篇文章有讲过,不再赘述
    const root = createContainer(
      container,
      LegacyRoot,
      null, // hydrationCallbacks
      false, // isStrictMode
      false, // concurrentUpdatesByDefaultOverride,
      '', // identifierPrefix
      noopOnRecoverableError, // onRecoverableError
      null, // transitionCallbacks
    );

    // 添加属性
    container._reactRootContainer = root; //此时maybeRoot才会有值
    // 标记
    markContainerAsRoot(root.current, container);

    // container
    const rootContainerElement =
      container.nodeType === COMMENT_NODE ? container.parentNode : container;
    // 事件委托处理
    // 这个函数上篇文章有讲过,不再赘述
    listenToAllSupportedEvents(rootContainerElement);

    // 初始化不应该批量挂载
    // 返回的结果要么是undefined | updateContainer()的结果
    flushSync(() => {
      //进入updateContainer函数
      updateContainer(initialChildren, root, parentComponent, callback);
    });

    return root;
  }
}

updateContainer(未完)

export function updateContainer(
  element: ReactNodeList, //app
  container: OpaqueRoot, //ReactDOMRoot
  parentComponent: ?React$Component<any, any>, //null
  callback: ?Function,//callback
): Lane {

  // 获取current
  // 此时ReactDOMRoot.current就是FiberHostRoot对象
  const current = container.current;
  // 当前时间戳
  // 进入requestEventTime函数
  const eventTime = requestEventTime();
  
  .....

requestEventTime

export function requestEventTime() {
  // 判断有哪个权限
  // executionContext 1表示有,0表示无
  // RenderContext 1表示有,0表示无
  // CommitContext 1表示有,0表示无
  // 使用 | 来添加权限
  // 使用 & 来判断权限
  
  //这里就当我们不知道这些具体的值,我们假设没有通过这个if
  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
    return now();
  }
  //执行到这里,currentEventTime就是NoTimestamp
  //源码:let currentEventTime: number = NoTimestamp;
  if (currentEventTime !== NoTimestamp) {
    return currentEventTime;
  }
  //所以还是return的now()
  currentEventTime = now();
  return currentEventTime;
}

updateContainer(未完)

....
  // 创建一个优先级变量(车道模型)
  //来看requestUpdateLane这个函数是个啥
  const lane = requestUpdateLane(current);
....

requestUpdateLane

export function requestUpdateLane(fiber: Fiber): Lane {
  // 获取到当前渲染的模式:sync mode(同步模式) 或 concurrent mode(并发模式)
  const mode = fiber.mode;

fiber.mode是多少呢?

截屏2022-08-21 22.27.54.png

答案是3 那3代表什么?

1,普通模式,同步渲染

2,并发模式,异步渲染

3,严格模式,用来检测是否存在废弃API

4,性能测试模式,用来检测哪里存在性能问题

参考司徒正美大佬文章zhuanlan.zhihu.com/p/54037407

    ......
    
  //我们继续
  //这里已知mode=3 ConcurrentMode=1 所以不会走这个if
  if ((mode & ConcurrentMode) === NoMode) {
    return (SyncLane: Lane);
    
  } else if (
  //也不会走这个if
    !deferRenderPhaseUpdateToNextBatch &&
    (executionContext & RenderContext) !== NoContext &&
    workInProgressRootRenderLanes !== NoLanes
  ) {
    return pickArbitraryLane(workInProgressRootRenderLanes);
  }
  //requestCurrentTransition()返回null
  const isTransition = requestCurrentTransition() !== NoTransition;
  if (isTransition) {
    ....
  }
  //获取当前更新优先级,因为还没有进行相应的操作,所以当前优先级是NoLane 0
  const updateLane: Lane = (getCurrentUpdatePriority(): any);
  //不成立
  if (updateLane !== NoLane) {
    return updateLane;
  }
  /*
      这里简单说一下getCurrentEventPriority作用:获取window.event,
      如果是undefined那么返回DefaultEventPriority,反之拿着事件类型执行getEventPriority(),
      getEventPriority内部就是switch判断给原生事件分个类然后返回React事件类型
  */
  //这里返回的是DefaultEventPriority 16
  const eventLane: Lane = (getCurrentEventPriority(): any);
  return eventLane;

updateContainer(未完)

  //requestUpdateLane返回了16
  const lane = requestUpdateLane(current);
  
  .....
  
  // 因为parentComponent此时是null,所以context是emptyContextObject
  const context = getContextForSubtree(parentComponent);
  // 最开始container不存在context属性
  if (container.context === null) {
    // 赋值emptyContextObject
    container.context = context;
  } else {
    container.pendingContext = context;
  }
  
  // 根据车道优先级, 创建update对象, 并加入fiber.updateQueue.pending队列
  // 得到update={
    // eventTime,
    // lane,

    // // export const UpdateState = 0;
    // // export const ReplaceState = 1;
    // // export const ForceUpdate = 2;
    // // export const CaptureUpdate = 3;
    // tag: UpdateState,
    // payload: null,
    // callback: null,

    // next: null, //指向updateQueue中的下一个update
  // }
  const update = createUpdate(eventTime, lane);
  
  // element就是App
  update.payload = {element};
  
  // callback存在
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    ......
    // 将callback函数挂载到update对象上
    update.callback = callback;
  }
  
  //进入enqueueUpdate函数
  const root = enqueueUpdate(current, update, lane);
  
  .....

enqueueUpdate

export function enqueueUpdate<State>(
  fiber: Fiber,
  update: Update<State>, //update对象
  lane: Lane, //调度优先级
): FiberRoot | null {
  // 获取当前fiber身上的updateQueue对象
  // 当在初始化时,fiber.updateQueue是有值的
  // 在ReactFiberRoot.old.js 202行,initializeUpdateQueue()时进行了挂载queue
  /**
   * fiber.updateQueue: UpdateQueue<State> = {
      baseState: fiber.memoizedState, //前一次的计算结果
      firstBaseUpdate: null,//上一个更新流程中被跳过的开头的update
      lastBaseUpdate: null,//上一个更新流程中被跳过的结尾的update
      shared: { 
        // 指向最新 且完整的环状链条
        //         A1  -> A2  -> A3
        //          |             |
        // pending(A6) -> A5  -> A4
        // pending是一个完整的Queue
        pending: null,
        lanes: NoLanes,
        hiddenCallbacks: null,
      },
      callbacks: null,
    };
   */
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    // 如果是卸载,那么就返回null
    return null;
  }
  
  //在上篇文章的initializeUpdateQueue函数中queue对象上的share就是updateQueue.shared
  //因为queue最终挂载到了fiber.updateQueue上
  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
  if (isUnsafeClassRenderPhaseUpdate(fiber)) {
    // This is an unsafe render phase update. Add directly to the update
    // queue so we can process it immediately during the current render.
    /**
     * 以下操作就是:
     *  首次会自己指向自己
     *  后续就将循环指向,因为update也越来越多了,sharedQueue.pending永远是最新的
     *  pending.next则指向最老的update
     */
    const pending = sharedQueue.pending;// shared.pending指向该链表的最新的update对象
    if (pending === null) {
      // 说明是`首次更新`, 需要`创建`循环链表
      update.next = update;
    } else {
      // `不是首次更新`, 那就把update对象`插入`到循环链表中
      /**
       * chain = a -> b -> c
       * pending = c  pending.next = a
       * update = d d为新入队的任务
       * update.next = pending.next  即是a
       * pending.next = update; 即是d
       * 执行sharedQueue.pending = update; 
       * 此时pending为d,pending.next为a
       * 最终:a-> b -> c -> d
       */
      update.next = pending.next; 
      pending.next = update;
    }
    sharedQueue.pending = update;

    // Update the childLanes even though we're most likely already rendering
    // this fiber. This is for backwards compatibility in the case where you
    // update a different component during render phase than the one that is
    // currently renderings (a pattern that is accompanied by a warning).
    
    // return root
    return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
  } else {
    //但是根据debug走还是走的这里的return
    
    //暂时有点不懂enqueueConcurrentClassUpdate这个函数,后面看懂了我再更新一下
    return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
  }
}

updateContainer(完)

....
if (root !== null) {
    // scheduleWork 就是 scheduleUpdateOnFiber
    // 进入scheduleUpdateOnFiber函数
    scheduleUpdateOnFiber(root, current, lane, eventTime);
    // 进入entangleTransitions函数
    entangleTransitions(root, current, lane);
}

return lane;

scheduleUpdateOnFiber(未完)

export function scheduleUpdateOnFiber(
  root: FiberRoot, //root
  fiber: Fiber, //FiberHostRoot对象
  lane: Lane, //lane
  eventTime: number, //事件发生时间戳
) {
  ......
  
  // 标记root有待更新
  // 进入markRootUpdated函数
  markRootUpdated(root, lane, eventTime);
  ......

markRootUpdated

export function markRootUpdated(
  root: FiberRoot,
  updateLane: Lane,
  eventTime: number,
) {
  root.pendingLanes |= updateLane;

  //如果不是空闲lane
  if (updateLane !== IdleLane) {
    root.suspendedLanes = NoLanes;
    root.pingedLanes = NoLanes;
  }

  const eventTimes = root.eventTimes;
  // laneToIndex()计算lane的前导0的个数
  const index = laneToIndex(updateLane);
  eventTimes[index] = eventTime;
}

scheduleUpdateOnFiber(未完)

    .....
    //性能
    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
      ......
    }
    
    // 性能
    if (enableTransitionTracing) {
      ......
    }
    //workInProgressRoot:null
    //此时还没有开启双缓存树
    if (root === workInProgressRoot) {
      ......先不管
    }
    
    // 确保根被调度
    ensureRootIsScheduled(root, eventTime);

总结

暂时先讲到这里,下一篇文章将进入ensureRootIsScheduled函数,这个函数是React进去render阶段前的准备工作

这一部分主要是React获取到fiber后,获取eventTime,事件类型已经创建updateQueue