• ReactDOM.render在react源码中执行之后发生了什么?


    ReactDOM.render

    通常是如下图使用,在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)。本文主要是将ReactDOM.render的执行流程在后续文章中会对创建更新的细节进行分析,文中的源代码部分为了方便阅读将__DEV__部分的代码移除掉了。

    ReactDOM.render(
      <App />,
      document.getElementById('root')
    );
    
    • 1
    • 2
    • 3
    • 4

    render

    位于:react-dom/src/client/ReactDOMLegacy.js

    export function render(
      element: React$Element<any>,  container: Container,  callback: ?Function,
    ) {
      // 验证container是否为有效的DOM节点
      invariant(
        isValidContainer(container),
        'Target container is not a DOM element.',
      );
      return legacyRenderSubtreeIntoContainer(
        null,
        element,
        container,
        false,
        callback,
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    返回了一个legacyRenderSubtreeIntoContainer函数,这里注意有5个参数

    parentComponent: 父组件因为是初次创建所以为null。

    children: 传入的ReactElement

    container: 渲染React的DOM容器

    forceHydrate: 判断是否需要协调,在服务端渲染的情况下已渲染的DOM结构是类似的因此可以在对比后进行复用。在服务端渲染的情况下使用ReactDOM.hydrate()与 render() 相同只是forceHydrate会标记为true。

    callback: 渲染完成后的回调函数

    legacyRenderSubtreeIntoContainer

    位于:react-dom/src/client/ReactDOMLegacy.js
    作用:

    • 判断是否为初次渲染,如果是就创建root并将root._internalRoot赋值给fiberRoot同时封装callback回调,然后调用unbatchedUpdates立即更新子节点。
    • 如果不是第一次渲染则进入正常的updateContainer流程。
    • 最后getPublicRootInstance(fiberRoot)返回公开的 Root 实例对象。
    function legacyRenderSubtreeIntoContainer(
      parentComponent: ?React$Component<any, any>,
      children: ReactNodeList,
      container: Container,
      forceHydrate: boolean,
      callback: ?Function,
    ) {
    
      // TODO: Without `any` type, Flow says "Property cannot be accessed on any
      // member of intersection type." Whyyyyyy.
      let root: RootType = (container._reactRootContainer: any);
      let fiberRoot;
      if (!root) {
        // Initial mount 初次渲染创建FiberRoot
        root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
          container,
          forceHydrate,
        );
        fiberRoot = root._internalRoot;
        if (typeof callback === 'function') {
          const originalCallback = callback;
          callback = function() {
            const instance = getPublicRootInstance(fiberRoot);
            originalCallback.call(instance);
          };
        }
        // Initial mount should not be batched.
        unbatchedUpdates(() => {
          updateContainer(children, fiberRoot, parentComponent, callback);
        });
      } else {
        fiberRoot = root._internalRoot;
        if (typeof callback === 'function') {
          const originalCallback = callback;
          callback = function() {
            const instance = getPublicRootInstance(fiberRoot);
            originalCallback.call(instance);
          };
        }
        // Update
        updateContainer(children, fiberRoot, parentComponent, callback);
      }
      return getPublicRootInstance(fiberRoot);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    相关参考视频讲解:进入学习

    legacyCreateRootFromDOMContainer

    位于:react-dom/src/client/ReactDOMLegacy.js
    初次渲染进入创建root的环节:root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate)
    作用:主要是判断是否为服务端渲染,如果是的话就会复用存在的dom节点进行协调(reconciliation)提高性能,如果不是则会清空container中的子元素,最后传入container和shouldHydrate返回createLegacyRoot函数。

    function legacyCreateRootFromDOMContainer(
      container: Container,  forceHydrate: boolean,
    ): RootType {
      const shouldHydrate =
        forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // 判断是否是服务端渲染
      // First clear any existing content.
      if (!shouldHydrate) {
        let warned = false;
        let rootSibling;
        while ((rootSibling = container.lastChild)) {
          container.removeChild(rootSibling);
        }
      }
    
      return createLegacyRoot(
        container,
        shouldHydrate
          ? {
              hydrate: true,
            }
          : undefined,
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    createLegacyRoot

    位于:react-dom/src/client/ReactDOMRoot.js
    作用:返回了一个ReactDOMBlockingRoot实例,这里传入了LegacyRoot是一个常量=0代表着现在使用的同步渲染模式,是为了后续的Concurrent可中断渲染模式做准备。

    export function createLegacyRoot(  container: Container,  options?: RootOptions, // hydrate
    ): RootType {
      return new ReactDOMBlockingRoot(container, LegacyRoot, options);
    }
    
    • 1
    • 2
    • 3
    • 4

    ReactDOMBlockingRoot

    位于:react-dom/src/client/ReactDOMRoot.js
    作用:将createRootImpl函数的返回(FiberRoot)挂载到实例的_internalRoot上

    function ReactDOMBlockingRoot(
      container: Container,  tag: RootTag,  options: void | RootOptions,
    ) {
      this._internalRoot = createRootImpl(container, tag, options);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    createRootImpl

    位于:react-dom/src/client/ReactDOMRoot.js
    作用:执行createContainer拿到FiberRootNode并赋值给root,再通过markContainerAsRoot将RootFiber挂载到container上。

    function createRootImpl(
      container: Container,
      tag: RootTag,
      options: void | RootOptions,
    ) {
      // Tag is either LegacyRoot or Concurrent Root
      const hydrate = options != null && options.hydrate === true;
      const hydrationCallbacks =
        (options != null && options.hydrationOptions) || null;
        // 拿到FiberRootNode
      const root = createContainer(container, tag, hydrate, hydrationCallbacks);
      // 将FiberRootNode挂载到container
      markContainerAsRoot(root.current, container);
      if (hydrate && tag !== LegacyRoot) {
        const doc =
          container.nodeType === DOCUMENT_NODE
            ? container
            : container.ownerDocument;
        eagerlyTrapReplayableEvents(container, doc);
      }
      return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    createContainer

    位于:react-reconciler/src/ReactFiberReconciler.old.js 作用:返回createFiberRoot

    export function createContainer(  containerInfo: Container,  tag: RootTag,  hydrate: boolean,  hydrationCallbacks: null | SuspenseHydrationCallbacks,): OpaqueRoot {
      return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
    }
    
    • 1
    • 2
    • 3

    createFiberRoot

    位于:react-reconciler/src/react-reconciler/src/ReactFiberReconciler.old.js 作用: 新建FiberRoot对象并赋值给root,初始化Fiber(通常叫做RootFiber)通过root.current = uninitializedFiber和uninitializedFiber.stateNode = root将两者联系起来。
    执行initializeUpdateQueue(uninitializedFiber)创建一个更新队列,挂载fiber.updateQueue下面
    最后将root返回

    export function createFiberRoot(  containerInfo: any,  tag: RootTag,  hydrate: boolean,  hydrationCallbacks: null | SuspenseHydrationCallbacks,): FiberRoot {
      // 新建fiberRoot对象
      const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
      if (enableSuspenseCallback) {
        root.hydrationCallbacks = hydrationCallbacks;
      }
    
      // Cyclic construction. This cheats the type system right now because
      // stateNode is any.
      //初始化RootFiber
      const uninitializedFiber = createHostRootFiber(tag);
      root.current = uninitializedFiber;
      // RootFiber的stateNode指向FiberRoot
      uninitializedFiber.stateNode = root;
    
      initializeUpdateQueue(uninitializedFiber);
    
      return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    FiberRoot RootFiber 和 updateQueue

    ReactDOM.render主要创建了三个对象FiberRooat、RootFiber和Updatequeue下面我们这对这三个对象进行分析

    FiberRoot

    FiberRoot是FiberRootNode(containerInfo, tag, hydrate)的实例
    位于:react-reconciler/src/ReactFiberRoot/FiberRootNode
    作用:

    • 整个应用的起点
    • 包含应用挂载的目标节点
    • 记录整个应用更新过程的各种信息
    function FiberRootNode(containerInfo, tag, hydrate) {
      // 标记不同的组件类型
      this.tag = tag;
      // 当前应用对应的Fiber对象,是Root Fiber
      // current:Fiber对象 对应的是 root 节点,即整个应用根对象
      this.current = null;
      // root节点,render方法接收的第二个参数
      this.containerInfo = containerInfo;
       // 只有在持久更新中会用到,也就是不支持增量更新的平台,react-dom不会用到
      this.pendingChildren = null;
      this.pingCache = null;
    
      //任务有三种,优先级有高低:
      //(1)没有提交的任务
      //(2)没有提交的被挂起的任务
      //(3)没有提交的可能被挂起的任务
    
       //当前更新对应的过期时间
      this.finishedExpirationTime = NoWork;
      //已经完成任务的FiberRoot对象,如果你只有一个Root,那么该对象就是这个Root对应的Fiber或null
      //在commit(提交)阶段只会处理该值对应的任务
      this.finishedWork = null;
      // 在任务被挂起的时候通过setTimeout设置的返回内容,用来下一次如果有新的任务挂起时清理还没触发的timeout(例如suspense返回的promise)
      this.timeoutHandle = noTimeout;
      // 顶层context对象,只有主动调用renderSubTreeIntoContainer时才会被调用
      this.context = null;
      this.pendingContext = null;
      // 第一次渲染是否需要调和
      this.hydrate = hydrate;
      // Node returned by Scheduler.scheduleCallback
      this.callbackNode = null;
      this.callbackPriority = NoPriority;
      //存在root中,最旧的挂起时间
      //不确定是否挂起的状态(所有任务一开始均是该状态)
      this.firstPendingTime = NoWork;
      this.firstSuspendedTime = NoWork;
      this.lastSuspendedTime = NoWork;
      this.nextKnownPendingLevel = NoWork;
      //存在root中,最新的挂起时间
      //不确定是否挂起的状态(所有任务一开始均是该状态)
      this.lastPingedTime = NoWork;
      this.lastExpiredTime = NoWork;
      this.mutableSourcePendingUpdateTime = NoWork;
    
      if (enableSchedulerTracing) {
        this.interactionThreadID = unstable_getThreadID();
        this.memoizedInteractions = new Set();
        this.pendingInteractionMap = new Map();
      }
      if (enableSuspenseCallback) {
        this.hydrationCallbacks = null;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    RootFiber

    RootFiber初始化于const uninitializedFiber = createHostRootFiber(tag) 通过 createFiber 返回 FiberNode的实例 作用:

    • 每个ReactElement对应一个Fiber对象
    • 记录节点的各种状态(方便了hooks,因为记录state和props都是在Fiber只是完成后再挂载到this的例如:pendingProps pendingState memoizedProps memoizedState)
    • 串联整个应用形成树结构
    // 位于 react-reconciler/src/ReactFiber.js
    export function createHostRootFiber(tag: RootTag): Fiber {
      let mode;
      if (tag === ConcurrentRoot) {
        mode = ConcurrentMode | BlockingMode | StrictMode;
      } else if (tag === BlockingRoot) {
        mode = BlockingMode | StrictMode;
      } else {
        mode = NoMode;
      }
    
      return createFiber(HostRoot, null, null, mode);
    }
    
    const createFiber = function(
      tag: WorkTag,
      pendingProps: mixed,
      key: null | string,
      mode: TypeOfMode,
    ): Fiber {
      return new FiberNode(tag, pendingProps, key, mode);
    };
    
    // FiberNode结构
    function FiberNode(
      tag: WorkTag,
      pendingProps: mixed,
      key: null | string,
      mode: TypeOfMode,
    ) {
      // Instance
      // 标记不同的组件类型
      this.tag = tag;
      // ReactElement里面的key
      this.key = key;
      // ReactElement.type,也就是我们调用`createElement`的第一个参数
      this.elementType = null;
      // 异步组件lazy component resolved之后返回的内容,一般是`function`或者`class`组件
      this.type = null;
      // 对应节点的实例,比如类组件就是class的实例,如果是dom组件就是dom实例,如果是function component就没有实例这里为空
      this.stateNode = null;
    
      // Fiber Fiber是个链表通过child和Sibling连接,遍历的时候先遍历child如果没有子元素了则访问return回到上级查询是否有sibling
      // 指向他在Fiber节点树中的‘parent’,用来在处理完这个节点之后向上返回
      this.return = null;
      // 指向第一个子节点
      this.child = null;
      // 指向自己的兄弟节点,兄弟节点的return指向同一个副节点
      this.sibling = null;
      this.index = 0;
    
      this.ref = null;
      // 新的变动带来的新的props
      this.pendingProps = pendingProps;
      // 上次渲染完成后的props
      this.memoizedProps = null;
      // 该Fiber对应的组件产生的update会存放在这个队列(比如setState和forceUpdate创建的更新)
      this.updateQueue = null;
      // 上一次的state
      this.memoizedState = null;
    
      this.dependencies = null;
      // 
      this.mode = mode;
    
      // Effects
      // 用来记录副作用
      this.effectTag = NoEffect;
      // 单链表用来快速查找下一个side effect
      this.nextEffect = null;
      // 子树中第一个side effect
      this.firstEffect = null;
      // 子树中最后一个side effect
      this.lastEffect = null;
    
      // 代表任务在未来的哪个时候应该被完成 就是过期时间
      // 不包括他的子树产生的任务
      this.expirationTime = NoWork;
      // 快速确定子树中是否有不再等待的变化
      this.childExpirationTime = NoWork;
    
      // Fiber树更新过程中,每个FIber都会有一个跟其对应的Fiber
      // 我们称他为`current <==> workInProgress`
      // 渲染完成后他们会交换位置
      this.alternate = null;
    
      // 调试相关的去掉了
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    updateQueue

    initializeUpdateQueue(uninitializedFiber);
    位于:react-reconciler/src/ReactUpdateQueue.js
    作用:单向链表,用来存放update,next来串联update
    关于Update和UpdateQueue涉及到的东西比较多打算单独一章来讲解

    export function initializeUpdateQueue<State>(fiber: Fiber): void {
      const queue: UpdateQueue<State> = {
        // 每次操作完更新阿之后的state
        baseState: fiber.memoizedState,
        // 队列中的第一个`Update`
        firstBaseUpdate: null,
        // 队列中的最后一个`Update`
        lastBaseUpdate: null,
        shared: {
          pending: null,
        },
        effects: null,
      };
      fiber.updateQueue = queue;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    流程图

    最后是画的大致流程图

  • 相关阅读:
    ESD防静电监控系统后台实时掌控现场静电防护情况
    RFSoC应用笔记 - RF数据转换器 -08- RFSoC关键配置之RF-DAC内部解析(二)
    Kafka部署、原理和使用介绍
    云服务连续三年增长150%,网宿科技开拓新赛道
    MATLAB求极限
    记录在windows下安装MySQL所遇到的各种坑
    基于VggNet网络与ResNet神经网络的物体分类识别研究-附Matlab代码
    200. 岛屿数量
    SQL优化
    IO-Link通信工业RFID智能传感器|读写器在工业自动化领域的优势分析
  • 原文地址:https://blog.csdn.net/weixin_59558923/article/details/127859195