fiber 节点与 FiberRootNode - HostRootFiber

react 代码

js 复制代码
import { createRoot } from "react-dom/client";

console.log("main");

const element = (
  <h1>
    hello <span>test</span> children
  </h1>
);

console.log(element);

// 调用 createRoot 函数生成 root
const root = createRoot(document.getElementById("root"));

root.render(element);

FiberRootNode 与 HostRootFiber

FiberRootNode 指向 HostRootFiber,HostRootFiber 也指向 FiberRootNode。

FiberRootNode

根据 createRoot 函数的调用链,整理出 FiberRootNode 的实现。 代码中删除了无关逻辑。

react-dom/client.js 复制代码
// 入口文件,导出 createRoot 函数
export { createRoot } from "./src/ReactDOMRoot";
ReactDOMRoot.js 复制代码
// 删减无关代码
import { createContainer } from "react-reconciler/src/ReactFiberReconciler";

// ReactDOMRoot 构造函数
function ReactDOMRoot(internalRoot) {
  this._internalRoot = internalRoot;
}
export function createRoot(container) {
  // 创建 root
  const root = createContainer(container);
  // 返回 ReactDOMRoot 实例
  return new ReactDOMRoot(root);
}
ReactFiberReconciler.js 复制代码
// 调用 createFiberRoot 方法
import { createFiberRoot } from "./ReactFiberRoot";
export function createContainer(containerInfo) {
  return createFiberRoot(containerInfo);
}
ReactFiberRoot.js 复制代码
// FiberRootNode 构造函数
function FiberRootNode(containerInfo) {
  this.containerInfo = containerInfo;
}
// 返回 FiberRootNode 实例
export function createFiberRoot(containerInfo) {
  const root = new FiberRootNode(containerInfo);
  return root;
}

最终发现,root 是一个 ReactDOMRoot 实例。数据结构如下。

可以看到,在 ReactDOMRoot 实例中,ReactDOMRoot._internalRoot = FiberRootNode。

HostRootFiber

HostRootFiber 按这个命名来说,HostRootFiber 是一个 fiber 节点,且是根 fiber 节点。根据上图 HostRootFiber 和 FiberRootNode 的关系可以知道,应该在创建 FiberRootNode 时也初始化 HostRootFiber。

createFiberRoot 复制代码
export function createFiberRoot(containerInfo) {
  const root = new FiberRootNode(containerInfo);
  // 创建根fiber
  const uninitializedFiber = createHostRootFiber(root);
  // root.current 指当前 fiber 树
  root.current = uninitializedFiber;
  // 对于 DOM 元素, stateNode 为 fiber 节点对应的真实 DOM 节点。
  // 对于函数式组件, stateNode 为 fiber 节点对应的函数返回值。
  // 对于类组件, stateNode 为 fiber 节点对应的组件实例。
  uninitializedFiber.stateNode = root;
  return root;
}
createHostRootFiber 复制代码
import { HostRoot } from "./ReactWorkTags";
import { NoFlags } from "./ReactFiberFlags";
/**
 *  Fiber 节点
 * @param {*} tag fiber 类型: 函数组件 类组件 原生标签 根
 * @param {*} pendingProps 等待渲染的属性
 * @param {*} key 唯一标识
 */
export function FiberNode(tag, pendingProps, key) {
  this.tag = tag;
  this.key = key;
  this.type = null; // fiber 对应虚拟DOM 节点的类型 例如:h1 span
  this.stateNode = null; // 对应真实 DOM 节点

  this.return = null; // 指向父节点
  this.child = null; // 指向子节点
  this.sibling = null; // 指向兄弟节点

  this.pendingProps = pendingProps; // 等待生效的属性
  this.memoizedProps = null; // 已经生效的属性

  this.memoizedState = null; // 状态
  this.updateQueue = null; // 更新队列

  this.alternate = null; // fiber 轮替节点

  // 副作用标识指操作类型, 增删改,react 内部使用位运算计算副作用类型
  this.flags = NoFlags; // fiber 本身对应副作用标识
  this.subtreeFlags = NoFlags; // 子树对应副作用标识
}

export function createFiber(tag, pendingProps, key) {
  return new FiberNode(tag, pendingProps, key);
}

export function createHostRootFiber() {
  return createFiber(HostRoot, null, null);
}

FiberRootNode 与 HostRootFiber 的关系就实现了。

这里有一个点,在实现根 fiber 的时候,没有用到虚拟DOM。正常理解,虚拟DOM、fiber 和真实DOM的关系是先有虚拟DOM,再根据虚拟DOM实现 fiber 树,在根据 fiber 树渲染真实DOM。但是这里直接就渲染出了根 fiber ,这是因为根节点的真实DOM是直接给的,对应的是 index.html 中的 div。

updateQueue

先实现一个简单的 updateQueue。

initialUpdateQueue 复制代码
export function initialUpdateQueue(fiber) {
  const queue = {
    shared: {
      pending: null, // 循环链接,指向链表中最后一个 update
    },
  };
  fiber.updateQueue = queue;
}

结束

这篇文章主要梳理 createRoot 函数执行逻辑。

相关推荐
ttod_qzstudio3 分钟前
Vue 3 Props 定义详解:从基础到进阶
前端·vue.js
钱端工程师4 分钟前
uniapp封装uni.request请求,实现重复接口请求中断上次请求(防抖)
前端·javascript·uni-app
dcloud_jibinbin6 分钟前
【uniapp】解决小程序分包下的json文件编译后生成到主包的问题
前端·性能优化·微信小程序·uni-app·vue·json
茶憶9 分钟前
uniapp移动端实现触摸滑动功能:上下滑动展开收起内容,左右滑动删除列表
前端·javascript·vue.js·uni-app
Ayn慢慢12 分钟前
uni-app PDA焦点录入实现
前端·javascript·uni-app
一位搞嵌入式的 genius22 分钟前
微前端架构:JavaScript 隔离方案全解析(含 CSS 隔离)概要
前端·css·前端实战
4_0_424 分钟前
一步一步实现 Shader 水波纹效果(入门到进阶)
前端·three.js
lemonboy24 分钟前
可视化大屏适配方案:用 Tailwind CSS 直接写设计稿像素值
前端·vue.js
鹏仔工作室24 分钟前
vue中实现1小时不操作则退出登录功能
前端·javascript·vue.js
海云前端126 分钟前
前端必备 Nginx 实战指南 8 个核心场景直接抄
前端