React18代码的探索(一)

253 阅读9分钟

一般我们都会从createRoot开始入手分析react的源码 ReactDOM.createRoot(rootElement).render();

create

流程图

graph TD
A[createRoot] --> B{判断是否合法容器}
B -->|Yes| C[createContainer 创建容器]
B -->|No| D[报错]
C -->E[createFiberRoot 创建根节点,实例一个根fiber]
E -->F[createHostRootFiber 创建主机节点并指向根节点的current]
F -->G[initializeUpdateQueue 初始化根节点进入更新队列]

1. createRoot

实际调用的是createRootImpl方法,传入创建的节点和参数

     // 创建根节点的工具函数
    function createRoot(
      container: Element | Document | DocumentFragment,
      options?: CreateRootOptions,
    ): RootType {
     ...
      return createRootImpl(container, options);
    }

2. createRootImpl

负责调用创建容器方法,根据enableNewReconciler判断调用createContainer_new还是createContainer_old; enableNewReconciler的主要作用是启用React的新Reconciler实现

该容器将负责将虚拟 DOM(即组件树)与真实 DOM 对接起来

createContainer,通过enableNewReconciler 判断是否开去新调和旧的reconciler,后续通过createFiberRoot方法去创建根节点

function createRootImpl(
      container: Element | Document | DocumentFragment,
      options?: CreateRootOptions,
    ) {
     //首先判断是否合法容器
     //options 设置变量
     ...
     const root = createContainer(
      container,
      ConcurrentRoot,
      null,
      isStrictMode,
      concurrentUpdatesByDefaultOverride,
      identifierPrefix,
      onRecoverableError,
      transitionCallbacks,
      );
    }
    
     listenToAllSupportedEvents(rootContainerElement);

      return new ReactDOMRoot(root);
  
    

3. createFiberRoot

创建根节点,返回一个FiberRoot对象

//fiberRootNode是一个根结点构造函数,创建的是一个实例
function FiberRootNode(
  containerInfo,
  tag,
  hydrate,
  identifierPrefix,
  onRecoverableError,
) {
  this.tag = tag; // 根节点标签
  this.containerInfo = containerInfo; // 容器信息
  this.pendingChildren = null; // 挂起的子节点
  this.current = null; // 当前 Fiber 节点
  this.pingCache = null; // ping 缓存
  this.finishedWork = null; // 已完成的工作
  this.timeoutHandle = noTimeout; // 超时句柄
  this.context = null; // 上下文
  this.pendingContext = null; // 挂起的上下文
  this.callbackNode = null; // 回调节点
  this.callbackPriority = NoLane; // 回调优先级
  this.eventTimes = createLaneMap(NoLanes); // 事件时间
 this.expirationTimes = createLaneMap(NoTimestamp); // createLaneMap 存储过期时间的映射,react 中lane 是用来标识更新优先级的位掩码,它可以在频繁运算的时候占用内存少,计算速度快。每个 lane 代表一种优先级级别 totalLanes = 31 初始值均为-1

  this.pendingLanes = NoLanes; // 挂起的 Lane
  this.suspendedLanes = NoLanes; // 挂起的 Lane
  this.pingedLanes = NoLanes; // ping 的 Lane
  this.expiredLanes = NoLanes; // 过期的 Lane
  this.mutableReadLanes = NoLanes; // 可变的读取 Lane
  this.finishedLanes = NoLanes; // 已完成的 Lane

  this.entangledLanes = NoLanes; // 纠缠的 Lane
  this.entanglements = createLaneMap(NoLanes); // 纠缠关系

  this.identifierPrefix = identifierPrefix; // 标识符前缀
  this.onRecoverableError = onRecoverableError; // 可恢复错误的回调
  }
function createFiberRoot(
    containerInfo: Container,
    tag: RootTag,
    hydrate: boolean,
  ){
    //创建FiberRoot节点
    const root = new FiberRootNode(containerInfo, tag, hydrate);
    //创建一个主机根节点的Fiber节点,将root的current设置为该主机fiber
    const uninitializedFiber = createHostRootFiber(
      tag,
      isStrictMode,
      concurrentUpdatesByDefaultOverride,
    );
    // 建立FiberRoot与RootFiber之间的联系
    root.current = uninitializedFiber; // 设置当前 Fiber 节点
    uninitializedFiber.stateNode = root; // 设置 Fiber 节点的 stateNode
    ...
     // 对于HostRoot或者ClassComponent会使用initializeUpdateQueue创建updateQueue, 
     // 然后将updateQueue挂载到fiber节点上
     initializeUpdateQueue(uninitializedFiber); 
     return root;
  }
  
    //创建一个主机根节点的Fiber节点
  function createHostRootFiber(
    tag: RootTag,
    isStrictMode: boolean,
    concurrentUpdatesByDefaultOverride: boolean | null,
  ){
    //创建一个新节点
    return createFiber(HostRoot, null, null, mode);
  }

4.createFiber

创建节点 ,返回一个fiber对象 fiber对象通过return、sibling、child指向,来构建完整的fiber树,各个节点包含了类型、父级、子级、兄弟节点、lanes级别(调度级别)、alternate(备用节点)等属性

 function createFiber(
    tag: WorkTag,
    pendingProps: mixed,
    key: null | string,
  ){
    return  new FiberNode(tag, pendingProps, key, mode);
  }
  
  /**
 * FiberNode 构造函数,用于创建一个新的 Fiber 节点。
 * @param {WorkTag} tag - Fiber 节点的标签,表示节点的类型
 * @param {mixed} pendingProps - 挂起的属性
 * @param {null | string} key - 节点的 key
 * @param {TypeOfMode} mode - 节点的模式
 */
 function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // 实例属性
  this.tag = tag; // 节点标签
  this.key = key; // 节点 key
  this.elementType = null; // 元素类型
  this.type = null; // 节点类型
  this.stateNode = null; // 节点对应的 DOM 节点或组件实例

  // Fiber 属性
  this.return = null; // 父节点 通过这个属性来构建一个树状结构,每个节点都有一个父节点和子节点,通过这个属性可以找到父节点和子节点
  this.child = null; // 第一个子节点 通过这个属性可以找到第一个子节点
  this.sibling = null; // 兄弟节点 通过这个属性可以找到兄弟节点
  this.index = 0; // 节点在父节点中的索引 通过这个属性可以找到节点在父节点中的索引

  this.ref = null; // 节点的 ref 

  this.pendingProps = pendingProps; // 挂起的属性
  this.memoizedProps = null; // 记忆的属性
  this.updateQueue = null; // 更新队列
  this.memoizedState = null; // 记忆的状态
  this.dependencies = null; // 依赖项

  this.mode = mode; // 节点模式

  // 副作用标志
  this.flags = NoFlags; // 节点的标志
  this.subtreeFlags = NoFlags; // 子树的标志
  this.deletions = null; // 待删除的节点

  this.lanes = NoLanes; // 节点的 Lane
  this.childLanes = NoLanes; // 子节点的 Lane

  this.alternate = null; // 备用节点
}

5.initializeUpdateQueue

当根节点创建成功后,需要初始化更新队列,给fiber添加updateQueue更新属性。 updateQueue是React状态更新相关的数据结构

其中shared.pending保存当前待执行的更新,比如1 ==> 2,其中baseState就是1,pending就是2

我们后面可以通过优先级lane来调度这些更新,执行对应的待执行pending

//初始化更新队列
function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState, //当前fiber的初始化状态
    firstBaseUpdate: null, //初始化更新链表指针
    lastBaseUpdate: null,
    shared: {
      pending: null, //创建共享更新队列(环形链表结构)
      lanes: NoLanes, //初始化lane
    },
    effects: null,//初始化副作用数组
  };
  fiber.updateQueue = queue;
}

到此创建了根节点FiberRoot,他是fiber结构的最外层对象,在应用里至少有一个,它通过current属性与rootFiber关联。

rootFiber表示组件对应的fiber对象,可以有多个,通过stateNode指向fiberRoot

我们创建完根节点后,初始化创建了一个更新对象,用于存放初始值优先级待执行更新

紧接着,react会调用listenToAllSupportedEvents托管浏览器方法对其实行监听

6.listenToAllSupportedEvents

是React事件系统的核心逻辑之一,主要负责在DOM根节点上注册所有支持的事件监听器

listeningMarker是是否正在监听标识,判断是否存在可以防止重复监听

react通过事件注册系统,给allNativeEvents注册事件

SimpleEventPlugin.registerEvents(); // 基础事件(click/keypress等)
EnterLeaveEventPlugin.registerEvents(); // 鼠标进入/离开事件
ChangeEventPlugin.registerEvents(); // 处理表单变更事件
SelectEventPlugin.registerEvents();  // 处理<select>元素事件
BeforeInputEventPlugin.registerEvents();  // 处理输入前事件

nonDelegatedEvents定义了不能代理的事件,如下

//定义nonDelegatedEvents 不能通过委托方式处理的事件
nonDelegatedEvents: Set<DOMEventName> = new Set([
  'cancel',
  'close',
  'invalid',
  'load',
  'scroll',
  'toggle',
  ...mediaEventTypes,
]);

listenToAllSupportedEvents 函数主要遍历各个事件,进行对应的分发处理

针对非selectionchange的事件,进行冒泡和捕获阶段的监听 selectionchange事件,对冒泡进行监听

本质实际到最后还是通过addEventListener对事件进行监听

export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
    //防止重复监听 
  if (rootContainerElement[listeningMarker]) {
    return;
  }
  rootContainerElement[listeningMarker] = true;
  //遍历 allNativeEvents 集合(包含所有React支持的原生事件)
  allNativeEvents.forEach(domEventName => {
    if (domEventName !== 'selectionchange') {
        //1. 双阶段监听 对每个事件同时注册:
        //冒泡阶段(capture=false)
      if (!nonDelegatedEvents.has(domEventName)) {
        //通过 nonDelegatedEvents 集合过滤无法冒泡的事件(如load/reset等)
        listenToNativeEvent(domEventName, false, rootContainerElement);
      }
      // 捕获阶段(capture=true)
      listenToNativeEvent(domEventName, true, rootContainerElement);
    }
  });
      
      //判断非selectionchange事件
   const ownerDocument =
      (rootContainerElement: any).nodeType === DOCUMENT_NODE
        ? rootContainerElement
        : (rootContainerElement: any).ownerDocument;
    if (ownerDocument !== null) {
      if (!(ownerDocument: any)[listeningMarker]) {
        (ownerDocument: any)[listeningMarker] = true;
        listenToNativeEvent('selectionchange', false, ownerDocument);
      }
    }
}

//桥接函数
function listenToNativeEvent(
  domEventName: DOMEventName,
  isCapturePhaseListener: boolean,
  target: EventTarget,
): void {
  let eventSystemFlags = 0;
  //定义是否冒泡
  if (isCapturePhaseListener) {
    eventSystemFlags |= IS_CAPTURE_PHASE;
  }
  //监听
  addTrappedEventListener(
    target,
    domEventName,
    eventSystemFlags,
    isCapturePhaseListener,
  );
}

function addTrappedEventListener(
  targetContainer: EventTarget,
  domEventName: DOMEventName,
  eventSystemFlags: EventSystemFlags,
  isCapturePhaseListener: boolean,
  isDeferredListenerForLegacyFBSupport?: boolean,
) {
  // 核心逻辑分为四个阶段:
  // 1. 创建带优先级的事件监听器包装函数
  let listener = createEventListenerWrapperWithPriority(
    targetContainer,
    domEventName,
    eventSystemFlags,
  );

  // 2. 处理被动事件优化(针对 touch/wheel 等特定事件)
  let isPassiveListener = undefined;
  if (passiveBrowserEventsSupported) {
    if (domEventName === 'touchstart' || ... ) {
      isPassiveListener = true;
    }
  }

  // 3. 处理遗留的 Facebook 支持模式
  if (enableLegacyFBSupport && ... ) {
    // 创建自动移除的一次性监听器
    listener = function(...p) {
      removeEventListener(...);
      return originalListener(...);
    };
  }

  // 4. 最终绑定事件监听器
  if (isCapturePhaseListener) {
    // 捕获阶段监听逻辑
    unsubscribeListener = addEventCaptureListenerWithPassiveFlag(...);
  } else {
    // 冒泡阶段监听逻辑
    unsubscribeListener = addEventBubbleListenerWithPassiveFlag(...);
  }
}

function addEventCaptureListenerWithPassiveFlag(
  target: EventTarget,
  eventType: string,
  listener: Function,
  passive: boolean,
): Function {
   //最终事件绑定
  target.addEventListener(eventType, listener, {
    capture: true,
    passive,
  });
  return listener;
}

addTrappedEventListener函数在 React 事件系统中的核心作用:

  1. 事件优先级分层 通过 createEventListenerWrapperWithPriority 实现三级优先级:
  • DiscreteEvent(离散事件:click/keydown 等)
  • UserBlockingEvent(用户阻塞事件:mouseover 等)
  • ContinuousEvent(持续事件:load/error 等)
export function createEventListenerWrapperWithPriority(
  targetContainer: EventTarget,
  domEventName: DOMEventName,
  eventSystemFlags: EventSystemFlags,
): Function {
  const eventPriority = getEventPriority(domEventName);
  let listenerWrapper;
  switch (eventPriority) {
    case DiscreteEventPriority:
      // 离散事件, 例如 click, change, submit 等
      listenerWrapper = dispatchDiscreteEvent;
      break;
    case ContinuousEventPriority:
      // 连续事件, 例如 mousemove, scroll 等
      listenerWrapper = dispatchContinuousEvent;
      break;
    case DefaultEventPriority:
    default:
      // 默认事件, 例如 load, error 等
      listenerWrapper = dispatchEvent;
      break;
  }
  return listenerWrapper.bind(
    null,
    domEventName,
    eventSystemFlags,
    targetContainer,
  );
}
  1. 被动事件优化 对 touchstart / touchmove / wheel 等影响滚动的关键事件:
// 强制设置为 passive:true 来优化滚动性能
isPassiveListener = true;
  1. 特殊事件处理 通过 eventSystemFlags 参数实现特殊事件标记:
// 事件系统标记示例
const IS_CAPTURE_PHASE = 0b0001; // 捕获阶段
const IS_PASSIVE = 0b0010;       // 被动模式
const IS_LEGACY_MODE = 0b0100;   // 传统支持模式

最终通过addEventListener来对各种事件进行绑定

回顾下,create阶段就是创建了系统根节点FiberRootNode,并且与根节点RootFiber通过current进行了绑定 通过事件优先级,将具体的元素事件都绑定到了根节点

至此,我们完成了创建Rootfiber和fiberRoot,初始化了更新队列,完成了浏览器事件托管监听,接下来调用render进入协调阶段

render阶段

18版本后render

new ReactDOMRoot(root)实例后,重写了ReactDOMRoot.prototype.render 方法

render调用 updateContainer方法,自动更新container

ReactDOMRoot.prototype.render = function(
  children: ReactNodeList,
): void {
  const root = this._internalRoot;
  updateContainer(children, root, null, null);
};

和老版本的ReactDom.render是一样,本质上都是调用updateContainer,只不过我们18版本后提前createApp创建了根节点

18版本前 ReactDom.render入口

实际调用legacyRenderSubtreeIntoContainer,默认开启异步渲染

function render(element: React$Element<any>, container: DOMContainer, callback?: ?Function) {
  return legacyRenderSubtreeIntoContainer(
    null, //父组件,首次渲染时为 null, 表示没有父组件
    element, // 要渲染的元素
    container, // 容器
    false, // 是否同步渲染,false 表示异步渲染
    callback, // 渲染完成后的回调函数
  );
}

1.legacyRenderSubtreeIntoContainer

负责将具体的组件子树渲染到指定的DOM容器中,此一次启动应用_reactRootContainer为null,默认创建一个新的root

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  forceHydrate: boolean,
  callback: ?Function,
) {
  // 1. 找到根节点
 const maybeRoot = container._reactRootContainer;
 let root: FiberRoot;

  if (!maybeRoot) {
    // 2. 没有根节点,创建一个新的根节点
    root = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
  }else {
    // 3. 有根节点,直接使用
    root = maybeRoot;
    // 更新容器
    // 进入reconciler 协调器 阶段
    updateContainer(children, root, parentComponent, callback);
  }
  //获取 Fiber 树的公共根实例
  //作为 React 内部与外部(如 ReactDOM)的桥梁
  return getPublicRootInstance(root);
}

2.legacyCreateRootFromDOMContainer

用来将一个已有的DOM元素转换为一个React的根节点

function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  isHydrationContainer: boolean,
) {
  if(isHydrationContainer){ 
    // 是否是 hydration 容器
    // 创建一个水合容器
    // 可以在服务端渲染(SSR)中使用,用于将服务端渲染的 HTML 与 React 组件进行合并,生成最终的 HTML
    // 例如next.js框架
    const root = createHydrationContainer(container);
    container._reactRootContainer = root;
    markContainerAsRoot(root.current, container);

    const rootContainerElement =
      container.nodeType === COMMENT_NODE ? container.parentNode : container;
    listenToAllSupportedEvents(rootContainerElement);

    flushSync();
    return root;
  }else{
    // 2. 不是 hydration 容器,创建一个新容器
    // createFiberRoot
    const root = createContainer(container);
    //添加_reactRootContainer 
    container._reactRootContainer = root;
    //标记__reactContainer
    markContainerAsRoot(root.current, container);
    //判断容器类型 注释容器-〉使用其父节点作为根容器 负责 直接使用原容器
    const rootContainerElement =
      container.nodeType === COMMENT_NODE ? container.parentNode : container;
     //在此真实DOM节点上绑定所有支持的事件
    listenToAllSupportedEvents(rootContainerElement);

    // 初始挂载不应批量处理
    flushSync(() => {
      //更新容器
      updateContainer(initialChildren, root, parentComponent, callback);
    });
    return root;

  }
}

3.flushSync

flushSync是 React 调度系统中的关键同步刷新机制,主要功能是强制同步执行更新

export function flushSync(fn) {
  // 保存原始执行上下文
  const prevExecutionContext = executionContext;
  //上下文切换 :通过 BatchedContext 标志进入批量更新模式
  executionContext |= BatchedContext;

  try {
    if (fn) {
      // 同步执行回调函数
      return syncUpdates(() => {
        return fn();
      });
    } else {
      return undefined
    }
  } finally {
    // 恢复执行上下文
    executionContext = prevExecutionContext;
     if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
        //立即处理所有同步回调 确保在浏览器绘制前完成关键状态更新
      flushSyncCallbacks();
    }
  }
}

flushSync → flushSyncCallbacks → 执行同步回调 → 触发同步渲染

后面我们具体讲updateContainer

React18代码的探索(二)