[源码-react01] ReactDOM.render01

1,232 阅读13分钟

ReactDOM.render.png

导航

[react] Hooks

[封装01-设计模式] 设计原则 和 工厂模式(简单抽象方法) 适配器模式 装饰器模式
[封装02-设计模式] 命令模式 享元模式 组合模式 代理模式

[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend

[源码-vue06] Vue.nextTick 和 vm.$nextTick
[源码-react01] ReactDOM.render01

[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[数据结构和算法01] 二分查找和排序

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例
[深入24] Fiber

[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
[前端学java06-SpringBoot实战] 注入 + Swagger2 3.0 + 单元测试JUnit5
[前端学java07-SpringBoot实战] IOC扫描器 + 事务 + Jackson
[前端学java08-SpringBoot实战总结1-7] 阶段性总结
[前端学java09-SpringBoot实战] 多模块配置 + Mybatis-plus + 单多模块打包部署
[前端学java10-SpringBoot实战] bean赋值转换 + 参数校验 + 全局异常处理
[前端学java11-SpringSecurity] 配置 + 内存 + 数据库 = 三种方式实现RBAC
[前端学java12-SpringSecurity] JWT
[前端学java13-SpringCloud] Eureka + RestTemplate + Zuul + Ribbon

(一) 前置知识

(1) 一些单词

queue 队列 
enqueue 排队 // enqueueSetState
lane 车道
batch 批次 // 批次更新 batched
internal 内部的 // key._reactInternals 保留字段
immutable 不可变 永恒的 // immutable.js
mutable 可变的 // an immutable object with a single mutable value
condition 条件 状态

seal 密封 封闭 // Object.seal 不能添加,删除,但是可以修改
freeze 冻结 // Object.freeze 不能添加,删除,修改

legacy blocking concurrent
// blocking 阻塞 块
// concurrent 并发

reuse 复用
hack 黑客,也有浏览器兼容的意思

pure 纯净的
shallow 浅的 // shallowEqual 
flush 清空,刷新,水洗

process 加工 进程
weak 弱的 // WeakMap
passive 被动的 消极的

(2) React新老生命周期对比

  • 老生命周期 image.png
  • 新生命周期 image.png
  • 新生命周期中
    • 1.废除了 componentWillMount componentWillReceiveProps componentWillUpdate
    • 2.新增了 getDerivedStateFromProps 和 getSnapshotBeforeUpate 来取代上面三个生命周期,注意:上面1和2的新老生生命周期不能混用
    • 3.新增了几个不常用的生命周期 componentDidCatch, getDerivedStateFromError

(3) PureComponent 和 React.memo 和 shouldComponentUpdate

  • PureComponent
    • 只能用于类组件
    • 浅比较了 props 和 state 的前后值,浅比较(===)
    • 如果 ( 相等返回false,不重新渲染 ) ( 不相等返回true,重新渲染 )
  • React.memo
    • React.memo(functionComponent, areEqual)
    • function areEqual(prevProps, nextProps) {}
    • 只能用于函数式组件
    • 只浅比较了 props 的前后值,浅比较(===)
    • 如果 ( 相等返回true,不重新渲染 ) ( 不相等返回false,重新渲染 )
  • shouldComponentUpdate
    • shouldComponentUpdate(nextProps, nextState)
    • 返回true重新渲染,返回false不重新渲染

image.png

(4) Object.seal() 和 Object.freeze()

  • Objext.seal()
    • ( 密封 ) 一个对象,( 阻止添加新属性 ),并将所有现有属性标记为 ( 不可配置 - 即属性不可被删除 ),当前属性的值,只要原来是可以写的就 ( 可以改变 )
    • 简单的说就是:( 不能添加/删除属性,但是可以修改属性 )
    • 参数: 将要密封的对象
    • 返回值:被密封的对象
    • seal 是密封的意思
  • Object.freeze()
    • ( 冻结 ) 对象,不能 ( 添加,修改,删除 ) 对象的属性
    • 参数: 将要密封的对象
    • 返回值:被密封的对象
Object.seal()
---
const obj = {name: 'woow_wu7'}
const obj2 = Object.seal(obj)

obj1 === obj2 // true
delete obj.name // false
delete obj2.name // -------- false,不能删除属性
obj2.name = 'wu7' // ------- 可以修改已有属性
obj.age = 20 // ------------ obj和obj2中都不会有age属性,不能添加属性
Object.freeze()
---

const obj = {name: 'woow_wu7'} 
const obj2 = Object.freeze(obj)

obj1 === obj2 // true
delete obj.name // -------- false,不能删除
obj.mame = 'wu7' // ------- 不能修改
obj.age = 20 // ----------- 不能添加

(5) React是如何区分 classComponent 和 functionComponent 的?

  • isReactComponent
    • Component.prototype.isReactComponent = {};
      • 在 classComponent 类的prototype上挂载了 isReactComponent 属性
      • 如果 isReactComponent 存在,则是classComponent
  • isPureReactComponent
    • 用来标记是否是 pureComponent image.png

(6) React源码中的一些概念

  • react中的 ( 三种模式 )
    • legacy
      • 当前react的使用方式,没有计划清除,但该模式可能以后不会支持新功能
      • ReactDOM.render(<App />, rootNode)
    • blocking
      • 目前正在试验中,作为迁移到 concurrent 模式的过渡
      • ReactDOM.createBlockingRoot(rootNode).render(<App />)
      • blocking 阻塞 块
    • concurrent
      • 目前正在试验中,以后将作为react的默认开发模式,该模式开启所有新功能
      • ReactDOM.createRoot(rootNode).render(<App />)
      • concurrent 并发
  • work
    • 在react reconciliation过程中出现的各种必须执行的活动叫作work
    • 比如 ( state update ) ( props update ) ( ref update )
  • WorkTag -- fiber.tag
    • 用来描述react元素的类型,也就是fiber对象的类型,即 ( fiber.tag )
  • side effects
    • 不能在render阶段完成的work,我们称这些工作为side effects
    • 比如:手动更改DOM或在生命周期中执行数据请求、订阅等操作,
  • effectTag -- fiber.effectTag
    • 除了update等常见的effect以外,fiber节点提供了一种方便的机制去追踪effect。
    • 每一个fiber节点都有一个和它相关联的effct,这些被编码为effectTag字段对应的值,即为fiber.effectTag
  • Effect list
    • effect list 是由 firstEffect,nextEffect,lastEffect 构成的链表结构
    • 重点如下
      • render阶段结束,会生成一个effect list
      • commit阶段,react会遍历effect list,并检查fiber对象的effect类型,当发现和其他类型相关用途的函数就调用
  • stateNode
    • 一个组件、一个DOM节点或其他跟fiber节点相关联的React元素的实例的引用
    • 通常,我们可以说这个属性是用于保存与一个fiber相关联的本地状态
    • fiber.stateNode

(7) Performance.now() 和 Date.now()

  • performance.now()
    • 返回从 ( time-origin ) 到 ( 当前调用经过的时间 )
      • 一个精确到毫秒的 DOMHighResTimeStamp
      • DOMHighResTimeStamp 是一个double类型,用于存储毫秒级的时间值
  • Date.now()
    • 返回当前时间距离时间零点的毫秒数,时间零点是1970/1/1/00:00:00
    • Date.now() === +new Date() === new Date - 0
performance.now
--
const t0 = window.performance.now(); // 启始时间
doSomething();
const t1 = window.performance.now(); // 终结时间
console.log("doSomething函数执行了" + (t1 - t0) + "毫秒.")

(二) Component 和 PureComponent

(2.1) PureComponent

  • 概念
    • PureComponent = Component + shouldComponentUpdate(nextProps, nextState)
    • 在 shouldComponentUpdate() 中对 ( 新旧props ) 和 ( 新旧state ) 做了 ( 一层钱比较 )
    • 一般这样使用:class A extends React.Component/React.PureComponent {}
  • 什么是 ( 浅比较 )
    • 浅比较也称 ( 引用相等 ),也就是全等 ( === )
 const o1 = {a: 1}
 const o2 = {a: 1}
 const o3 = o2
 const o4 = (obj) => { obj.b = 1; return obj }

 o1 === o2 // false
 o3 === o2 // true,两个变量,栈内存中的指针都指向了同一个堆内存数据的地址
 o4(o1) === o1 // true

(2.2) PureComponent 源码

  • isPureReactComponent
    • 相对于Component,在prototype上多了 isPureReactComponent=true 属性
    • 文件位置:packages/react/src/ReactBaseClasses.js
function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;

  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject; // 赋值空对象

  this.updater = updater || ReactNoopUpdateQueue;
  // ReactNoopUpdateQueue
  // - 是一个对象,具有这些属性 {isMounted, enqueueForceUpdate, enqueueReplaceState, enqueueSetState }
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
// 1
// new ComponentDummy()
// function ComponentDummy() {}
// ComponentDummy.prototype = Component.prototype;
// 寄生原型链继承,这样 ( pureComponent实例 ) 的 ( 原型对象上 ) 是没有任何属性的,但是原型对象的原型链上的属性仍然可以继承

// 2
// Component.prototype
// - 具有以下属性
// - isReactComponent
// - setState
// - forceUpdate

pureComponentPrototype.constructor = PureComponent;
// 2
// 修改prototype对象时,一定要修改prototype.constructor,防止引用出错
// 如果不修改,prototype.constructor将指向 ComponentDummy

// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
// 合并属性

pureComponentPrototype.isPureReactComponent = true;
// 3
// isPureReactComponent 用来判断是不是纯组件 PureComponent
  • checkShouldComponentUpdate
    • 文件位置:packages/react-reconciler/src/ReactFiberClassComponent.old.js
    • checkShouldComponentUpdate 检查是否需要更新时,对新旧props和新旧state做了一层钱比较
function checkShouldComponentUpdate(
  workInProgress,
  ctor,
  oldProps,
  newProps,
  oldState,
  newState,
  nextContext,
) {
  ...
  // PureComponent2
  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
      // 浅比较 props 和 state
    );
  }
 ...
}

(三) createRef

(3.1) React.createRef() 源码

export function createRef(): RefObject {
  const refObject = { current: null }; // ref实例上具有 current 属性
  if (__DEV__) {
    Object.seal(refObject); // ---------- current 属性不能添加和删除,只能修改,在开发环境时
  }
  return refObject;
}

(3.2) React.createRef() 使用案例

class TestCreateRef extends React.Component {
    inputRef = React.createRef();
    componentDidMount() { this.inputRef.current.focus() }
    render() {
        return <input type="text" ref={this.inputRef} />
    }
}

(四) ReactDOM.render()

(4.1) 搭建react源码调试环境

1. 通过create-react-app 创建一个react项目
2. yarn run eject 弹出配置
3. 克隆react源码 git clone git@github.com:facebook/react.git
4. 将克隆下来的 ( 源码的packages文件夹 ) 放入创建好的react项目的 ( src/REACT_SOURCE_CODE_ANALYSIS ) 文件夹下

5. 修改webpack.config.js中的resolve中的alias配置
alias: {
        // Support React Native Web
        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
        'react-native': 'react-native-web',
        // Allows for better profiling with ReactDevTools
        ...(isEnvProductionProfile && {
          'react-dom$': 'react-dom/profiling',
          'scheduler/tracing': 'scheduler/tracing-profiling',
        }),
        ...(modules.webpackAliases || {}),
        // + 添加如下代码
        'react': path.resolve(__dirname, '../src/REACT_SOURCE_CODE_ANALYSIS/packages/react'),
        'react-dom': path.resolve(__dirname, '../src/REACT_SOURCE_CODE_ANALYSIS/packages/react-dom'),
        'shared': path.resolve(__dirname, '../src/REACT_SOURCE_CODE_ANALYSIS/packages/shared'),
        'react-reconciler': path.resolve(__dirname, '../src/REACT_SOURCE_CODE_ANALYSIS/packages/react-reconciler'),
}

6. 修改 config/env.js
  const stringified = {
    __DEV__: true,
    __PROFILE__: true,
    __UMD__: true,
    __EXPERIMENTAL__: true, // 和 .eslintrc.json中的globals一一对应
    'process.env': Object.keys(raw).reduce((env, key) => {
      env[key] = JSON.stringify(raw[key]);
      return env;
    }, {}),
  };
  
 7. react项目根目录创建 .eslintrc.json
 {
  "extends": "react-app",
  "globals": {
    "__DEV__": true,
    "__PROFILE__": true,
    "__UMD__": true,
    "__EXPERIMENTAL__": true,
  }
}

8. 删除 webpack.config.js 中的 eslint 检查
// !disableESLintPlugin &&
      //   new ESLintPlugin({
      //     // Plugin options
      //     extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
      //     formatter: require.resolve('react-dev-utils/eslintFormatter'),
      //     eslintPath: require.resolve('eslint'),
      //     failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
      //     context: paths.appSrc,
      //     cache: true,
      //     cacheLocation: path.resolve(
      //       paths.appNodeModules,
      //       '.cache/.eslintcache'
      //     ),
      //     // ESLint class options
      //     cwd: paths.appPath,
      //     resolvePluginsRelativeTo: __dirname,
      //     baseConfig: {
      //       extends: [require.resolve('eslint-config-react-app/base')],
      //       rules: {
      //         ...(!hasJsxRuntime && {
      //           'react/react-in-jsx-scope': 'error',
      //         }),
      //       },
      //     },
      //   }),
      
9. src/REACT_SOURCE_CODE_ANALYSIS/packages/react-reconciler/src/ReactFiberHostConfig.js
// 注释掉 
// import invariant from 'shared/invariant'; 
// invariant(false, 'This module must be shimmed by a specific renderer.'); 
// 添加此行 
// export * from "./forks/ReactFiberHostConfig.dom";

10. src/REACT_SOURCE_CODE_ANALYSIS/packages/shared/ReactSharedInternals.js
// 注释掉
// import * as React from 'react';
// 添加此行
import ReactSharedInternals from '../react/src/ReactSharedInternals';
// 注释掉
// ReactSharedInternals
// const ReactSharedInternals =
//   React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
export default ReactSharedInternals;

11. src/REACT_SOURCE_CODE_ANALYSIS/packages/shared/invariant.js
export default function invariant(condition, format, a, b, c, d, e, f) {
  // 添加下面的一行
  if (condition) return;
  throw new Error(
    "Internal React error: invariant() is meant to be replaced at compile " +
      "time. There is no runtime version."
  );
}

12. 在react中项目的入口文件index.js中更新引入react和reactDOM的方法
// import React from 'react';
// import ReactDOM from 'react-dom';
import * as React from 'react'; // 这里能够引用到,是因为在webpack.config.js中的resolve.alias中指定了别名的引用路径
import * as ReactDOM from 'react-dom';

13. 在vscode中下载 Debugger for Chrome 插件,并在launch.json中做如下配置
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome against localhost",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}"
    }
  ]
}

image.png

(4.2) ReactDOM.render() 基础知识

  • ReactDOM.render(element, container[, callback])
    • 作用
      • 初次渲染:在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回null)
      • 更新:如果React元素之前已经在container里渲染过,这将会对其执行 ( 更新操作 ),并仅会在必要时改变DOM以映射最新的React元素
      • 如果提供了第三个参数,表示在 ( 初次渲染或者更新 ) 完成后执行的 ( 回调函数 )
  • 参数中的element是什么?
    • element是一个 (react元素)
        1. react组件的返回值就是一个react元素 ,组件由元素构成
        1. react元素可以通过 React.createElement(type, config, children)来创建
        1. ( react元素 ) 本质上就是一个 ( javascript对象 )
    • React.createElement(type, config, children) 源码
  • ReactDOM.render()分为 ( 三个阶段 )
    • init初始化阶段
      • 主要负责生成fiber树的基础实体对象,比如fiberRoot,rootFiber
    • render阶段
      • 负责fiber树的创建,调度,调和等
    • commit阶段
      • 根据virtualDOM,渲染真实DOM

(4.3) ReactDOM.render() 源码

  • ReactDOM.render 返回 legacyRenderSubtreeIntoContainer()

// ----------------------------------------------------------------------------------------------------------- ReactDOM.render
// 【 起点 】render
// 1
// ReactDOM.render(element, container[, callback])
// - 1. 作用:
//    - 1. 初始渲染:在提供的 container 里面渲染一个React元素,并返回对该组件的引用,( 或者针对无状态组件返回null )
//    - 2. 更新:-- 如果React元素之前已经在 container 里渲染过,这将会对其执行 ( 更新操作 ),并仅仅会在必要时改变DOM以映射最新的React元素
// - 2. 参数
//    - element
//      - element表示的是一个react元素,react组件由element元素组成,element元素本质上是一个javascript对象,可以通过React.createElement生成
//      - React.createElement(type, config, children) => ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props) => element
//      - element对象具有
//          - 基本属性:type,key,ref,props,_owner,  $$typeof
//          - DEV环境多了:__store, __self, __source 属性
//    - container
//      - 属性,即组件或者element上的属性
//    - callback: 可选,在 ( 渲染或更新 ) 完成后,执行的回调函数
// - 3. 返回值
//    - legacyRenderSubtreeIntoContainer
//    - legacyRenderSubtreeIntoContainer( null, element, container, false, callback ) 调用的返回值就是render()函数的返回值
export function render(
  element: React$Element<any>,
  container: Container,
  callback: ?Function,
) {
  if (__DEV__) {
    console.error(
      'ReactDOM.render is no longer supported in React 18. Use createRoot ' +
        'instead. Until you switch to the new API, your app will behave as ' +
        "if it's running React 17. Learn " +
        'more: https://reactjs.org/link/switch-to-createroot',
    );
    // createRoot
    // - React 18中不再支持ReactDOM.render,请使用 createRoot() 代替
  }

  invariant(
    isValidContainerLegacy(container), // 合法的节点
    'Target container is not a DOM element.',
  );
  // export default function invariant(condition, format, a, b, c, d, e, f) {
  //   throw new Error(
  //     'Internal React error: invariant() is meant to be replaced at compile ' +
  //       'time. There is no runtime version.',
  //   );
  // }

  if (__DEV__) {
    const isModernRoot =
      isContainerMarkedAsRoot(container) &&
      container._reactRootContainer === undefined;
    // 1
    // isContainerMarkedAsRoot
    // export function isContainerMarkedAsRoot(node: Container): boolean {
    //   return !!node[internalContainerInstanceKey];
    // }
    // 2
    // internalContainerInstanceKey
    // const internalContainerInstanceKey = '__reactContainer$' + randomKey;
    // 3
    // randomKey
    // const randomKey = Math.random()
    // .toString(36)
    // .slice(2);


    if (isModernRoot) {
      console.error(
        'You are calling ReactDOM.render() on a container that was previously ' +
          'passed to ReactDOM.createRoot(). This is not supported. ' +
          'Did you mean to call root.render(element)?',
          // 您正在对以前传递给 ReactDOM.createRoot() 的容器使用 ReactDOM.render(),不支持这样做,你是想调用root.render(element)?
      );
    }
  }

  // 1
  // legacyRenderSubtreeIntoContainer()
  // render() 函数最终返回值 legacyRenderSubtreeIntoContainer()
  // legacy : 遗产  +  render: 渲染  +  subtree: 子树  +  into: 到 +  container: 容器
  // 2
  // react一共有三种模式
  // legacy
  // blocking
  // concurrent 以后react18中将默认开启并发模式
 
  return legacyRenderSubtreeIntoContainer(
    null, // parentComponent 父组件
    element, // children
    container, // container
    false, // forceHydrate 服务端渲染的标志
    callback, // callback
  );
}
  • legacyRenderSubtreeIntoContainer
    • 主要做了以下事情
      • 创建fiberRoot 和 rootFiber,并相互引用
      • 关联 continer 和 rootFiber.current
      • 事件相关
      • updateContainer()

// ----------------------------------------------------------------------------------------------------------- legacyRenderSubtreeIntoContainer
// 【0】 legacyRenderSubtreeIntoContainer
// 1
// 初次渲染 init mount
// - 1. 初次渲染 - 是没有 (老的虚拟DOM节点的 )
// - 2. 即初次渲染生成一个插入一个,但如果是更新阶段则要考虑很多情况了,比如移动,更新,插件等等
// 2
// 如何判断是不是初次渲染?
// - 因为初次渲染是没有老的虚拟DOM节点的,所以可以通过 ( root ) 来判断,即 ( let root = container._reactRootContainer )
// 2
// 初始化调用ReactDOM.render() 时的 ( 参数 ) 如下
// - legacyRenderSubtreeIntoContainer( null, element, container, false, callback, );
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>, // init => null
  children: ReactNodeList, // render 第一个参数 element
  container: Container, // render 第二个参数 container
  forceHydrate: boolean, // 服务器端渲染标识,初始化是false, init => false
  callback: ?Function, // 初次渲染或者更新后需要执行的回调,可选,基本不会使用
) {
  if (__DEV__) {
    topLevelUpdateWarnings(container);
    warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
  }

  let root = container._reactRootContainer;
  // root
  // 1. 声明 root
  // 2. 在container上生声明了_reactRootContainer属性
  // 3. init时,_reactRootContainer属性不存在,则 root = undefined
  // 4. container
  //    - 1
  //    - container
  //    - container是调用 reactDOM.render()时传入的第二个参数,即jsx挂载的容器,是真实的DOM
  //    - 下面生成fiberRoot的时候,  root = container._reactRootContainer = legacyCreateRootFromDOMContainer(),
  //    - 即在容器上挂载了 fiberRoot
  //    - 2
  //    - _reactRootContainer
  //    - container._reactRootContainer
  //    - react的项目中,可以通过 document.querySelector('#root')._reactRootContainer 来读取 _reactRootContainer
  //    - 可以看到:( fiberRoot的构造函 ) 数是 ( FiberRootNode(containerInfo, tag, hydrate) )
  //    - 3
  //    - _internalRoot
  //    - container._reactRootContainer._internalRoot 对应这 ( fiberRoot )
  //    - ( _internalRoot === fiberRoot )
  //    - internal 是内部的意思
  // 5. 先总结一下
  //    - root 就是 fiberRoot,因为后面会赋值给 fiberRoot 变量
  //    - fiberRoot 同样挂在了 container DOM节点上
  //    - fiberRoot对象 就是整个fiber树的 根节点 ( 其实每个DOM节点一定对应着一个fiber对象,所以DOM树和fiber数一一对应 )

  let fiberRoot: FiberRoot; // 缓存 old virtual DOM,用于对比
  // fiberRoot
  // 1. 注意区分 fiberRoot 和 rootFiber
  //    - fiberRoot 只有一个
  //    - rootFiber 可以有多个,就是一个普通的fiber节点
  // 2. 两者的关系
  //    - rootFiber.stateNode = fiberRoot
  //    - fiberRoot.current = rootFiber
  //    - 两者循环引用

  if (!root) {
    // Initial mount
    // ------------------------------------------------------------ 初始化mount阶段,即初次渲染,root不存在
    // 初次渲染 root 是不存在的,所以要创建生成一个root

    // 1
    // container
    // container._reactRootContainer.
    // container._reactRootContainer._internalRoot 指向的就是 fiberRoot
    // 2
    // fiberRoot === fiberRootNode对象
    // 3
    // 再次复习 fiberRoot 和 rootFiber
    // - fiberRoot
    //   - fiberRoot 关联的是真实的DOM容器节点
    // - rootFiber
    //   - 是虚拟DOM的根节点
    // - fiberRoot.current === rootFiber
    // - rootFiber.stateNode === fiberRoot
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate, // init => false
    );
    fiberRoot = root; // 缓存 old virtual DOM,用于对比
    if (typeof callback === 'function') { // 一般都不会指定callback,即一般不会进入if
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    // 初次渲染是非批量更新,可以保证 ( 更新效率与用户体验 )
    // 比如初次渲染希望更快的速速让用户看到 ui
    flushSyncWithoutWarningIfAlreadyRendering(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
    // flushSyncWithoutWarningIfAlreadyRendering
    // - 函数签名:flushSyncWithoutWarningIfAlreadyRendering(fn) => fn()

  } else {
    // ------------------------------------------------------------ 更新阶段
    fiberRoot = root;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    // 批量更新 batched update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}
  • updateContainer()
    • 主要负责
      • 获取当前 fiber 节点的 lane优先级,lane越小,优先级越高
      • 创建context
      • 创建 update对象,并入队
      • 调度当前节点的 rootFiber
// ----------------------------------------------------------------------------------------------------------- updateContainer
// 【1】updateContainer
// updateContainer(children, fiberRoot, parentComponent, callback);

// 1
// updateContainer主要做的事情
// 1. 获取当前 fiber 节点的 lane 优先级
// 2. 结合lane优先级,创建当前fiber节点的update对象,并将其入队
// 3. 调度当前节点 rootFiber

// 2
// 调用栈:render > legacyRenderSubtreeIntoContainer > updateContainer
export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot, // fiberRoot
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): Lane {
  if (__DEV__) {
    onScheduleRoot(container, element);
  }
  const current = container.current; // container.current = fiberRoot.current = rootFiber; 这里rootFiber=3时,说明是 hostRoot
  const eventTime = requestEventTime(); // 541304.0999999642
  const lane = requestUpdateLane(current); // 1
  // lane
  // - lane 表示优先级
  // - lane越小,表示优先级越高
  // - lane为二进制存储,一共31位,每个位为一个车道

  // performance.now()
  // - 返回:当前网页自从performance.timing.navigationStart到当前时间之间的毫秒数

  if (enableSchedulingProfiler) {
    // export const enableSchedulingProfiler = __PROFILE__ && __EXPERIMENTAL__;
    markRenderScheduled(lane);
  }

  const context = getContextForSubtree(parentComponent); // mount => 返回一个空对象
  if (container.context === null) { // mount => 容器上的context是空
    container.context = context; // 给 container.context = {}
  } else {
    container.pendingContext = context;
  }

  if (__DEV__) {
    if (
      ReactCurrentFiberIsRendering &&
      ReactCurrentFiberCurrent !== null &&
      !didWarnAboutNestedUpdates
    ) {
      didWarnAboutNestedUpdates = true;
      console.error(
        'Render methods should be a pure function of props and state; ' +
          'triggering nested component updates from render is not allowed. ' +
          'If necessary, trigger nested updates in componentDidUpdate.\n\n' +
          'Check the render method of %s.',
        getComponentNameFromFiber(ReactCurrentFiberCurrent) || 'Unknown',
      );
    }
  }

  const update = createUpdate(eventTime, lane); // 返回update对象,tag=0, lane=1
  // Caution: React DevTools currently depends on this property
  // being called "element".
  // 警告:React DevTools当前依赖于名为“element”的此属性。
  update.payload = {element};

  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    if (__DEV__) {
      if (typeof callback !== 'function') {
        console.error(
          'render(...): Expected the last optional `callback` argument to be a ' +
            'function. Instead received: %s.',
          callback,
        );
      }
    }
    update.callback = callback;
  }

  enqueueUpdate(current, update, lane); // 将update对象入队,形成环状链表
  // enqueueUpdate
  // enqueueUpdate主要用来添加一个共享队列sharedQueue,该队列可以和workInProgress和FiberRoot进行共享队列

  const root = scheduleUpdateOnFiber(current, lane, eventTime); // 调度 fiber 节点的挂载

  if (root !== null) {
    entangleTransitions(root, current, lane);
  }
 
  return lane; // 返回 ( 当前节点 即fiberRoot ) 的优先级
}

(五) 源码分析仓库地址

资料