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

293 阅读5分钟

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

前置知识

  • Fiber
  • React生命周期钩子

有关Fiber的内容,大家可以Google一下,网上有很多讲解,关于Fiber我有可能后面会写篇文章

本篇文章只是给大家讲解执行顺序和逻辑,真正执行时的数值结果等,还请大家自己debug一下查看实际执行结果

推荐的Debug方式

下载react-dom.development.js && react.development.js两个文件,然后引入html的方式直接开干,我自己感觉用脚手架比较麻烦,用文件引入的方式,只要文件和当前源码版本一直,那么基本上都是大同小异的

大家可以前往这个地址下载www.bootcdn.cn/

开始之前(必看)

React version 18.2.0

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

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

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

入口

const root = ReactDOM.createRoot(document.querySelector("#app"));
root.render(<App />);

createRoot

入口就是createRoot这个方法了

// 入口 最终返回ReactDOMRoot对象
function createRoot(
  container: Element | Document | DocumentFragment,
  options?: CreateRootOptions,//可选
  //可以看到createRoot其实可以接收第二个参数的,但是我们通常并没有使用到
): RootType {
  .....
  return createRootImpl(container, options);
  //进入createRootImpl
}

createRootImpl as createRoot(未完)

//这个createRoot不是上一个createRoot
export function createRoot(
  container: Element | Document | DocumentFragment,
  options?: CreateRootOptions,
): RootType {
  // 判断是否是有效容器
  if (!isValidContainer(container)) {
    throw new Error('createRoot(...): Target container is not a DOM element.');
  }

  ....

  
  //定义变量
  let isStrictMode = false;
  let concurrentUpdatesByDefaultOverride = false;
  let identifierPrefix = '';
  let onRecoverableError = defaultOnRecoverableError;
  let transitionCallbacks = null;

  .....

  // 创建容器对象
  // 最终返回的是具有current:FiberHostRoot的FiberRoot对象
  // 进入createContainer()
  const root = createContainer(
    container, //Element | Document | DocumentFragment,
    ConcurrentRoot, //export const ConcurrentRoot = 1;
    null,
    isStrictMode, //false
    concurrentUpdatesByDefaultOverride, //false
    identifierPrefix, //'';
    onRecoverableError, //全局错误报告函数
    transitionCallbacks, //null
  );
  
  .....

createContainer

export function  createContainer(
  containerInfo: Container,//Element | Document | DocumentFragment,
  tag: RootTag,//export const ConcurrentRoot = 1;
  hydrationCallbacks: null | SuspenseHydrationCallbacks, //null
  isStrictMode: boolean,  //false
  concurrentUpdatesByDefaultOverride: null | boolean,//false
  identifierPrefix: string,//''
  onRecoverableError: (error: mixed) => void,//全局错误报告函数
  transitionCallbacks: null | TransitionTracingCallbacks, //null
): OpaqueRoot {
  const hydrate = false;
  const initialChildren = null;
  
  // 返回挂载了current属性的FiberRoot对象
  // 进入createFiberRoot
  return createFiberRoot(
    containerInfo,
    tag,
    hydrate,
    initialChildren,
    hydrationCallbacks,
    isStrictMode,
    concurrentUpdatesByDefaultOverride,
    identifierPrefix,
    onRecoverableError,
    transitionCallbacks,
  );
}

createFiberRoot

export function createFiberRoot(
  containerInfo: any, 
  tag: RootTag,
  hydrate: boolean,
  initialChildren: ReactNodeList,
  hydrationCallbacks: null | SuspenseHydrationCallbacks,
  isStrictMode: boolean,
  concurrentUpdatesByDefaultOverride: null | boolean,
  identifierPrefix: string,
  onRecoverableError: null | ((error: mixed) => void),
  transitionCallbacks: null | TransitionTracingCallbacks,
): FiberRoot {
  //new FiberRootNode这里,就是new一个新的FiberRootNode对象,大家可以自己进入看下,
  //里面就是一些自身属性的声明,有点繁杂了,大家自己去看一下吧
  const root: FiberRoot = (new FiberRootNode(
    containerInfo,
    tag,
    hydrate,
    identifierPrefix,
    onRecoverableError,
  ): any);
  
  .....

  // 创建FiberHostRoot对象
  // FiberRootNode和FiberHostRoot是两个东西,不要混淆
  const uninitializedFiber = createHostRootFiber(
    tag,
    isStrictMode,
    concurrentUpdatesByDefaultOverride,
  );
  // 在FiberRootNode对象上挂载current属性,值为FiberHostRoot对象
  root.current = uninitializedFiber;
  // 在FiberHostRoot对象上挂载stateNode:FiberRootNode
  /*
    现在的样子大概就是:
    uninitializedFiber:{
        stateNode:{
            root:{
                current:FiberHostRoot,
                ....
            }
        }
    }
  */
  uninitializedFiber.stateNode = root;

  if (enableCache) {
    ......
  } else {
    const initialState: RootState = {
      element: initialChildren,//查看createContainer函数中,会发现是null
      isDehydrated: hydrate,//不管
      cache: (null: any), // not enabled yet
    };
    //memoizedState可以理解为`已经记录的state`
    //uninitializedFiber是什么,好好回想一下!
    //是FiberHostRoot!
    uninitializedFiber.memoizedState = initialState;
  }

  // 初始化updateQueue队列!
  // 进入initializeUpdateQueue
  initializeUpdateQueue(uninitializedFiber);

  ......

initializeUpdateQueue

export function initializeUpdateQueue<State>(fiber: Fiber/*FiberHostRoot*/): void {
  const queue: 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,
  };
  // 挂载queue
  fiber.updateQueue = queue;
}

执行完毕,返回上一个函数createFiberRoot

    .....
  // 返回挂载了FiberHostRoot对象的FiberRoot对象
  // 这里返回的是root 并不是 uninitializedFiber!!!
  return root;
}

执行完毕,返回上一个函数createContainer

继续返回上一个函数createRoot(createRootImpl)

createRootImpl as createRoot(未完)

.....
/**
   * 传入的root.current就是在createContainer内部创建的FiberHostRoot对象
   * 
   * markContainerAsRoot在当前container身上添加一个internalContainerInstanceKey属性
   * 值为root.current
   */
  markContainerAsRoot(root.current, container);

  // rootContainerElement: container
  const rootContainerElement: Document | Element | DocumentFragment =
    container.nodeType === COMMENT_NODE
      ? (container.parentNode: any)
      : container;
      
  // 事件委托处理
  // 进入listenToAllSupportedEvents
  listenToAllSupportedEvents(rootContainerElement);
.....

listenToAllSupportedEvents

export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
  // 判断rootContainerElement身上是否有listeningMarker属性
  if (!(rootContainerElement: any)[listeningMarker]) {
    // 如果没有,则添加为ture
    (rootContainerElement: any)[listeningMarker] = true;
    // allNativeEvents:获取所有原生事件
    allNativeEvents.forEach(domEventName => {
      // We handle selectionchange separately because it
      // doesn't bubble and needs to be on the document.
      if (domEventName !== 'selectionchange') {
        if (!nonDelegatedEvents.has(domEventName)) {
          listenToNativeEvent(domEventName, false, rootContainerElement);
        }
        listenToNativeEvent(domEventName, true, rootContainerElement);
      }
    });
    const ownerDocument =
      (rootContainerElement: any).nodeType === DOCUMENT_NODE
        ? rootContainerElement
        : (rootContainerElement: any).ownerDocument;
    if (ownerDocument !== null) {
      // The selectionchange event also needs deduplication
      // but it is attached to the document.
      if (!(ownerDocument: any)[listeningMarker]) {
        (ownerDocument: any)[listeningMarker] = true;
        listenToNativeEvent('selectionchange', false, ownerDocument);
      }
    }
  }
}

//进入listenToNativeEvent

listenToNativeEvent

export function listenToNativeEvent(
  domEventName: DOMEventName,
  isCapturePhaseListener: boolean, //false
  target: EventTarget, //根容器节点
): void {
  ......

  let eventSystemFlags = 0;
  // 判断是否是监听捕获阶段
  if (isCapturePhaseListener) {
    // 是的话,打上标记
    eventSystemFlags |= IS_CAPTURE_PHASE;
  }
  // 将事件绑定到根节点上
  // 进入addTrappedEventListener
  addTrappedEventListener(
    target,
    domEventName,
    eventSystemFlags,
    isCapturePhaseListener,
  );
}

addTrappedEventListener

function addTrappedEventListener(
  targetContainer: EventTarget, //根容器节点
  domEventName: DOMEventName,
  eventSystemFlags: EventSystemFlags, //事件flag
  isCapturePhaseListener: boolean, //false
  isDeferredListenerForLegacyFBSupport?: boolean,
) {
  // createEventListenerWrapperWithPriority()优先创建事件监听器包装器
  //返回:一个事件方法bind调用后的值
  let listener = createEventListenerWrapperWithPriority(
    targetContainer,
    domEventName,
    eventSystemFlags,
  );
  // If passive option is not supported, then the event will be
  // active and not passive.
  let isPassiveListener = undefined;
  if (passiveBrowserEventsSupported) {
    /**
     * 这里进行对一个特殊事件的判断,因为浏览器的干预,
     * 所以需要对touchstart|touchmove|wheel事件进行单独处理
     */
    if (
      domEventName === 'touchstart' ||
      domEventName === 'touchmove' ||
      domEventName === 'wheel'
    ) {
      isPassiveListener = true;
    }
  }

  // 不变
  targetContainer =
  /**
   * enableLegacyFBSupport:false
    初次调用addTrappedEventListener时没有传递isDeferredListenerForLegacyFBSupport的值,所以是null
    条件不成立,targetContainer还是targetContainer
    如果启用了传统FB那么targetContainer就是targetContainer.ownerDocument了
   */
    enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
      ? (targetContainer: any).ownerDocument
      : targetContainer;

  let unsubscribeListener;
  if (enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport) {
    const originalListener = listener;
    listener = function(...p) {
      //移除监听
      removeEventListener(
        targetContainer,
        domEventName,
        unsubscribeListener,
        isCapturePhaseListener,
      );
      return originalListener.apply(this, p);
    };
  }
  // TODO: There are too many combinations here. Consolidate them.
  if (isCapturePhaseListener) {
    if (isPassiveListener !== undefined) {
      unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
        targetContainer,
        domEventName,
        listener,
        isPassiveListener,
      );
    } else {
      ......
    }
  } else {
    ......
  }
}

执行完毕,返回上一层函数createRoot

createRootImpl as createRoot(完)

......
  // 返回ReactDomRoot对象,该对象的_internalRoot属性指向FiberRoot
  // 进入ReactDOMRoot
  return new ReactDOMRoot(root);
}

ReactDOMRoot

/**
 * 返回一个ReactDOMRott对象,
 * 该对象的_internalRoot属性指向FiberRoot
 * 这个函数就这么简单
 */
function ReactDOMRoot(internalRoot: FiberRoot) {
  this._internalRoot = internalRoot;
}

最后

总结一下createRoot(createRootImpl),主要就是创建Fiber,事件委托处理,最后将root身上添加一个属性_internalRoot,这个属性指向root

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

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