React 创建根节点 createRoot

话不多说,直接上代码。

入口函数 createRoot

js 复制代码
// 第一个参数 domNode 取值 Document、Element 或者 DocumentFragment
// 第二个参数 options 可选,参考 https://zh-hans.react.dev/reference/react-dom/client/createRoot
createRoot(domNode, options?){
  const root: FiberRoot = createContainer(params)

  // 标记 container 是根 Fiber
  // 这个函数给 container 根 DOM 节点赋值根 Fiber
  markContainerAsRoot(root.current, container)

  return new ReactDOMRoot(root)
}

进入 createRoot 函数体。

createContainer

先说下调用路线

  1. createContainer 调用 createFiberRoot
  2. createFiberRoot 调用 createHostRootFiberinitializeUpdateQueue
  3. createHostRootFiber 调用 createFiber
  4. createFiber 调用 FiberNode

对应下面 4 段代码。

js 复制代码
// 参数透传给 createFiberRoot
function createContainer(params) {
    createFiberRoot(params)
}
js 复制代码
function FiberRootNode() {
  //初始化 FiberRoot
  this.current = null
  this.next = null
  ...
}

type SharedQueue = {
  pending: Update<State> | null, // 单向循环链表
  ...
}

type UpdateQueue<State> = {
  baseState: State,
  // 单链表 firstBaseUpdate -> ... -> lastBaseUpdate
  firstBaseUpdate: Update<State> | null,
  // React 中更喜欢链表结构:
  // 1. updateQueue 单链表 & 循环链表
  // 2. fiber 单链表
  // 3. hooks 单链表
  // 4. 多个根节点 单链表
  // 一般情况下,单链表是不用记录尾节点,这里记录尾节点是为了快速比较两个单链表,用尾节点比较
  lastBaseUpdate: Update<State> | null,
  shared: SharedQueue<State>
}

// 初次渲染页面和类组件初次挂载的时候,调用函数 initializeUpdateQueue 来初始化 fiber.updateQueue
// 这里初始化 fiber.updateQueue。在 beginWork 阶段,updateHostRoot 中使用 processUpdateQueue 函数再具体赋值
function initializeUpdateQueue<State>(fiber: Fiber): void {
  // 初始化 queue
  // 一个 fiber 上会有多个 update,存起来后面批量更新
  const queue: UpdateQueue<State> = {
      baseState: fiber.memoizedState, // 初次渲染,存的是 Element;类组件,存的是初始的状态值
      // 单向循环链表
      firstBaseUpdate: null,
      laseBaseUpdate: null,
      shared: {
          pending: null,
          lanes: NoLanes,
          hiddenCallbacks: null
      },
      callbacks: null
  }
  fiber.updateQueue = queue
}

// FiberRoot 是 rootFiber 的类型
// React 中有两种 fiber,普通 fiber 对应类型 Fiber,根 Fiber 对应类型 FiberRoot
function createFiberRoot(): FiberRoot {
  const root: FiberRoot = new FiberRootNode()

  const uninitializedFiber = createHostRootFiber()

  // 循环构造
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  initializeUpdateQueue(uninitializedFiber)
  
  return root
}
js 复制代码
function createHostRootFiber(params):Fiber {
  createFiber(params)
}
js 复制代码
function FiberNode() {
  this.ref = null
  this.flag = NoFlags
  this.alternate = null
}

function createFiber() {
  new FiberNode()
}

markContainerAsRoot

顾名思义,标记 Container 为根 Fiber。

js 复制代码
const randomKey = Math.random().toString(36).slice(2) // 比如 xdzhvpd522c,其中 36 进制即 0-9 和 26 个英文字母
const internalContainerInstanceKey = '__reactContainer$' + randomKey

// 标记根节点
markContainerAsRoot(hostRoot: Fiber, node: Container) {
  node[internalContainerInstanceKey] = hostRoot
}

这个属性值在函数 getClosestInstanceFromNodegetInstanceFromNode 中会用于根据 DOM 取 Fiber 值。

对应的还有两个函数:

js 复制代码
// 取消标记,在 ReactDOMRoot.prototype.unmount 函数里调用
export function unmarkContainerAsRoot(node: Container): void {
  node[internalContainerInstanceKey] = null
}

// 检查是否被标记为根节点
export function isContainerMarkedAsRoot(node: Container): boolean {
  return !!node[internalContainerInstanceKey]
}

ReactDOMRoot

FiberRoot 放到内部参数 _internalRoot 上。

_internal 前缀的是 React 内部的参数。

js 复制代码
function ReactDOMRoot(internalRoot: FiberRoot) {
  this._internalRoot = internalRoot;
}
相关推荐
人工智能训练4 小时前
【极速部署】Ubuntu24.04+CUDA13.0 玩转 VLLM 0.15.0:预编译 Wheel 包 GPU 版安装全攻略
运维·前端·人工智能·python·ai编程·cuda·vllm
会跑的葫芦怪5 小时前
若依Vue 项目多子路径配置
前端·javascript·vue.js
2601_949593655 小时前
基础入门 React Native 鸿蒙跨平台开发:模拟智能音响
react native·react.js·harmonyos
xiaoqi9226 小时前
React Native鸿蒙跨平台如何进行狗狗领养中心,实现基于唯一标识的事件透传方式是移动端列表开发的通用规范
javascript·react native·react.js·ecmascript·harmonyos
jin1233226 小时前
React Native鸿蒙跨平台剧本杀组队消息与快捷入口组件,包含消息列表展示、快捷入口管理、快捷操作触发和消息详情预览四大核心功能
javascript·react native·react.js·ecmascript·harmonyos
烬头88218 小时前
React Native鸿蒙跨平台实现二维码联系人APP(QRCodeContactApp)
javascript·react native·react.js·ecmascript·harmonyos
pas1368 小时前
40-mini-vue 实现三种联合类型
前端·javascript·vue.js
摇滚侠8 小时前
2 小时快速入门 ES6 基础视频教程
前端·ecmascript·es6
珑墨8 小时前
【Turbo】使用介绍
前端
军军君019 小时前
Three.js基础功能学习十三:太阳系实例上
前端·javascript·vue.js·学习·3d·前端框架·three