这其实是一篇,通过逆向设计,帮助理解 “React是怎么运作的?“
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>
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 树了。
见 2 和 4。
// 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 的三个特点。