这其实是一篇,通过逆向设计,帮助理解 “React是怎么运作的?“

1、JSX 是怎么转变为 HTML的?

Q: 游览器只认识 html,不认识 JSX。那么 JSX 是怎么转变为 html 的?

A:

// JSX:
const element = <div className="box">Hello</div>;

// 🔽 by Babel 或其他编译器 → 虚拟 DOM
const element = React.createElement(
  'div', 
  { className: 'box' }, 
  'Hello'
);

// 🔽 调用 API: document.createElement, appendChild, insertBefore
<div class="box">Hello</div>

2、React 的更新有哪些阶段?

A:

// 从功能上看
两个阶段
Render 阶段:算要改什么 → Scheduler(调度器) + Reconciler(协调器)
Commit 阶段:真正去改 → Renderer(渲染器)

// 从数据结构拆分这两个阶段
Render 阶段
1、React 调用你的组件函数 App(),函数运行完毕,返回新的 Virtual DOM树。
2、Diff:Diff_Functoin(`新的 VDOM`+`旧的 Fiber 树` ){return `WorkInProgress Fiber树`}
3、打标签:如果发现 VDOM 和 Fiber 不一样(比如 className 变了),React 就在 WorkInProgress Fiber 上打一个 Update 的标签

Commit 阶段:
1、Renderer 渲染器读取构建好的 WIP Fiber 树;
2、执行指命令:它只看 Fiber 上的标签(Flags)。表如标签说更新,它旧更新。
3、状态持久化。更新完成后,WIP Fiber 树就变成了 Current Fiber 树了。

3、我想知道 VDOM 和 Fiber 树的数据结构长什么样?

见 2 和 4。

4、为什么需要 Fiber,只用一个 VDOM 不行吗?

// VDOM Example,一个大的虚拟 DOM 树
const element = {
  $$typeof: Symbol.for('react.element'), // 标识这是一个 React 元素,防止 XSS
  type: 'div',       // 标签名或组件函数
  key: null,         // 用于 Diff 列表优化
  props: {           // 属性
    className: 'container',
    children: [      // 递归的孩子节点
      { type: 'h1', props: { children: 'Title' } }
    ]
  },
  ref: null
};

// Fiber Tree,它有三个部分组成,负责三个 feature
// 1. 架构属性(构建链表),这是为了实现“可中断渲染”而设计的指针
// 每个 Fiber 节点对应一个 React 组件实例或 DOM 节点,它通过三个指针形成树结构
// child    → 第一个子节点
// sibling  → 下一个兄弟节点
// return   → 父节点
const fiber = {
  // 1. 实例相关
  tag: 1,            // 标记组件类型(函数组件、类组件、原生 DOM 等)
  key: null,
  type: 'div',       // 对应的 React Element 的 type
  stateNode: div,    // 【重要】指向真实的 DOM 节点(或者类组件实例)

  // 2. 构建链表的指针(替代递归)
  return: Fiber,     // 指向父节点
  child: Fiber,      // 指向第一个子节点
  sibling: Fiber,    // 指向下一个兄弟节点
  index: 0,          // 在兄弟节点中的索引
};

// 2. 状态与数据(记录变化)
// 这是为了支持 Hooks 和 State 更新。
const fiber = {
// 3. 数据相关
  pendingProps: {},  // 新的 props(从 React Element 拿来的)
  memoizedProps: {}, // 旧的 props(上一次渲染用的)
  
  memoizedState: {}, // 【重要】组件的状态(Hooks 链表就存在这里)
  updateQueue: {},   // 待执行的状态更新队列
}

// 3. 调度与副作用(执行任务)
// 这是为了支持 Diff 算法和 Commit 阶段。
const fiber = {
// 4. 副作用(Diff 的结果)
  flags: Placement,  // 标记要做什么操作(插入、更新、删除)。老版本叫 effectTag
  subtreeFlags: 0,   // 子树中是否有副作用(用于优化,如果没有就不进子树了)
  
  // 5. 双缓存(Time Slicing 的基础)
  alternate: Fiber,  // 指向旧树中对应的 Fiber 节点
}

A:

其实就是 Fiber 的三个特点。