🐭
鼠鼠准备开始从0到1开始实现react,加深对源码的理解
简单实现了下react初次渲染,代码版本:github.com/zhuxin0/min…
核心模块
1. React 层 (react.js)
- 导出基础组件类
Component - 导出 Hooks:
useReducer、useState - 作为用户 API 的入口点
2. ReactDOM 层 (react-dom.js)
createRoot(container): 创建根渲染容器RootDOMRoot: 根容器类,管理渲染实例updateContainer(): 启动容器更新流程
3. Fiber 架构层
ReactFiber.js
createFiber(): 创建 Fiber 节点- 定义 Fiber 节点数据结构
- 根据节点类型设置相应的 tag
ReactWorkTags.js
- 定义各种组件类型标签
HostComponent: 原生 DOM 标签FunctionComponent: 函数组件ClassComponent: 类组件HostText: 文本节点Fragment: 片段组件
4. 协调器层 (ReactFiberReconciler.js)
updateHostComponent(): 处理原生 DOM 节点reconcileChildren(): 协调子节点updateNode(): 更新 DOM 节点属性- 为不同类型组件提供更新函数
5. 调度器层 (ReactFiberWorkLoop.js)
scheduleUpdateOnFiber(): 调度 Fiber 更新performUnitOfWork(): 执行工作单元workLoop(): 可中断的工作循环commitRoot(): 提交阶段入口commitWork(): 执行 DOM 操作
初次渲染完整流程
graph TD
A["JSX 代码"] --> B["ReactDOM.createRoot(container)"]
B --> C["创建 RootDOMRoot 实例"]
C --> D["root.render(jsx)"]
D --> E["updateContainer(children, containerInfo)"]
E --> F["createFiber(children, containerInfo)"]
F --> G["创建根 Fiber 节点"]
G --> H["scheduleUpdateOnFiber(rootFiber)"]
H --> I["设置 wip = fiber, wipRoot = fiber"]
I --> J["requestIdleCallback(wookloop)"]
J --> K["开始工作循环 wookloop"]
K --> L{浏览器有空闲时间?}
L -->|是| M["performUnitOfWork()"]
L -->|否| K
M --> N["根据 fiber.tag 选择更新函数"]
N --> O1["HostComponent<br/>updateHostComponent()"]
N --> O2["HostText<br/>updateTextComponent()"]
N --> O3["FunctionComponent<br/>updateFunctionComponent()"]
N --> O4["ClassComponent<br/>updateClassComponent()"]
O1 --> P["创建/更新 DOM 节点"]
P --> Q["reconcileChildren()<br/>协调子节点"]
Q --> R["深度优先遍历"]
R --> S{有子节点?}
S -->|是| T["wip = wip.child"]
S -->|否| U{有兄弟节点?}
U -->|是| V["wip = wip.sibling"]
U -->|否| W["wip = wip.return"]
T --> M
V --> M
W --> X{wip 为 null?}
X -->|否| U
X -->|是| Y["协调阶段完成"]
Y --> Z["commitRoot(wipRoot)"]
Z --> AA["commitWork(fiber)"]
AA --> BB["递归提交所有 DOM 操作"]
BB --> CC["渲染完成"]
style A fill:#e1f5fe
style CC fill:#c8e6c9
style M fill:#fff3e0
style Z fill:#fce4ec
系统架构图
graph TB
subgraph "React 层"
A1["react.js<br/>- Component<br/>- useReducer<br/>- useState"]
end
subgraph "ReactDOM 层"
B1["react-dom.js<br/>- createRoot()<br/>- RootDOMRoot<br/>- updateContainer()"]
end
subgraph "Fiber 架构层"
C1["ReactFiber.js<br/>- createFiber()<br/>- Fiber 节点结构"]
C2["ReactWorkTags.js<br/>- 定义组件类型标签<br/>- HostComponent<br/>- FunctionComponent<br/>- ClassComponent<br/>- HostText"]
end
subgraph "协调器层"
D1["ReactFiberReconciler.js<br/>- updateHostComponent()<br/>- updateFunctionComponent()<br/>- updateClassComponent()<br/>- reconcileChildren()"]
D2["utils.js<br/>- 工具函数<br/>- updateNode()<br/>- 类型判断函数"]
end
subgraph "调度器层"
E1["ReactFiberWorkLoop.js<br/>- scheduleUpdateOnFiber()<br/>- performUnitOfWork()<br/>- workLoop()<br/>- commitRoot()"]
E2["scheduler/<br/>- 任务调度<br/>- minHeap<br/>- 优先级队列"]
end
subgraph "DOM 层"
F1["浏览器 DOM API<br/>- document.createElement()<br/>- appendChild()<br/>- 属性设置"]
end
A1 --> B1
B1 --> C1
C1 --> C2
C1 --> D1
D1 --> D2
D1 --> E1
E1 --> E2
E1 --> F1
style A1 fill:#e3f2fd
style B1 fill:#f3e5f5
style C1 fill:#e8f5e8
style C2 fill:#e8f5e8
style D1 fill:#fff3e0
style D2 fill:#fff3e0
style E1 fill:#fce4ec
style E2 fill:#fce4ec
style F1 fill:#f1f8e9
详细流程说明
1. 初始化阶段
// 创建根节点
const root = ReactDOM.createRoot(document.getElementById("root"));
// 开始渲染
root.render(jsx);
关键步骤:
createRoot()创建包含containerInfo的根对象render()调用updateContainer()开始更新流程- 返回
RootDOMRoot实例,包装内部根对象
2. Fiber 树构建阶段
// 在 updateContainer 中
const rootFiber = createFiber(children, {
type: containerInfo.nodeName.toLowerCase(),
stateNode: containerInfo,
})
Fiber 节点结构:
const fiber = {
type: vnode.type, // 组件类型
key: vnode.key, // React key
props: vnode.props, // 属性对象
stateNode: null, // DOM 节点或组件实例
child: null, // 第一个子 Fiber
sibling: null, // 下一个兄弟 Fiber
return: returnFiber, // 父 Fiber
flags: Placement, // 副作用标记
index: null, // 在父节点中的索引
alternate: null, // 旧 Fiber 引用
tag: null // 节点类型标签
};
关键特性:
- 每个 JSX 元素都转换为一个 Fiber 节点
- 使用链表结构连接父子兄弟关系
- 根据节点类型设置不同的
tag
3. 工作循环阶段
// 深度优先遍历
function performUnitOfWork() {
// 1. 处理当前节点
switch (wip.tag) {
case HostComponent:
updateHostComponent(wip);
break;
case FunctionComponent:
updateFunctionComponent(wip);
break;
case ClassComponent:
updateClassComponent(wip);
break;
case HostText:
updateTextComponent(wip);
break;
// ... 其他类型
}
// 2. 遍历子节点
if (wip.child) {
wip = wip.child;
return;
}
// 3. 遍历兄弟节点或回到父节点
let next = wip;
while (next) {
if (next.sibling) {
wip = next.sibling;
return;
}
next = next.return;
}
wip = null;
}
核心机制:
- 使用
requestIdleCallback在浏览器空闲时执行 - 深度优先遍历 Fiber 树
- 每个节点调用对应的更新函数
- 可中断的工作循环,保证不阻塞主线程
4. 协调阶段
// 原生标签处理
function updateHostComponent(wip) {
// 创建 DOM 节点
if (!wip.stateNode) {
const stateNode = document.createElement(wip.type)
wip.stateNode = stateNode
}
// 更新属性
updateNode(wip.stateNode, wip.props)
// 协调子节点
reconcileChildren(wip, wip.props.children)
}
reconcileChildren 流程:
function reconcileChildren(wip, children) {
let newFiber = null
let previousNewFiber = null
let arr = Array.isArray(children) ? children : [children]
for (let i = 0; i < arr.length; i++) {
newFiber = createFiber(arr[i], wip)
// 构建链表关系
if (previousNewFiber === null) {
wip.child = newFiber
} else {
previousNewFiber.sibling = newFiber
}
previousNewFiber = newFiber
}
}
处理流程:
- 为每个 Fiber 节点创建对应的 DOM 节点
- 设置节点属性和事件监听
- 通过
reconcileChildren处理子节点 - 构建 Fiber 树的父子兄弟关系
5. 提交阶段
function commitWork(fiber) {
if (!fiber) return
const { flags, stateNode } = fiber
let parentNode = fiber.return.stateNode
// 插入 DOM 节点
if (flags === Placement && stateNode) {
parentNode.appendChild(stateNode)
}
// 递归处理子节点和兄弟节点
commitWork(fiber.child)
commitWork(fiber.sibling)
}
关键操作:
- 一次性将所有 DOM 变更提交到页面
- 递归遍历整个 Fiber 树
- 根据
flags标记执行相应的 DOM 操作(插入、更新、删除) - 保证 DOM 操作的原子性
核心特性
1. Fiber 架构
- 可中断渲染:使用时间切片,避免长时间阻塞主线程
- 链表结构:通过 child、sibling、return 指针连接节点
- 双缓冲:current 树和 workInProgress 树交替工作
2. 两阶段提交
- 协调阶段(Reconciliation):可中断,构建 Fiber 树,标记副作用
- 提交阶段(Commit):同步执行,一次性应用所有 DOM 变更
3. 深度优先遍历
- 确保父节点在子节点之前处理
- 使用 "国王的故事" 遍历算法
- 支持工作单元的暂停和恢复
4. 类型系统
通过 tag 区分不同类型的组件:
HostComponent (5): 原生 DOM 标签HostText (6): 文本节点FunctionComponent (0): 函数组件ClassComponent (1): 类组件Fragment (7): 片段组件
5. 副作用系统
使用位运算标记不同类型的副作用:
Placement (2): 插入操作Update (4): 更新操作Deletion (8): 删除操作
使用示例
import { ReactDOM } from "./which-react";
const jsx = (
<div className="border">
<h1>react</h1>
<a href="https://github.com/bubucuo/mini-react">mini react</a>
</div>
);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(jsx);
已实现功能
- ✅ 原生 DOM 节点渲染
- ✅ 属性和事件处理
- ✅ 文本节点渲染
- ✅ 基础 Fiber 架构
- ✅ 可中断的工作循环
- ✅ 两阶段提交
待完善功能
- ⏳ 函数组件支持
- ⏳ 类组件支持
- ⏳ Hooks 系统
- ⏳ Fragment 组件
- ⏳ 更新和删除操作
- ⏳ 事件系统
- ⏳ 错误边界
这个简易 React 实现虽然功能有限,但已经包含了 React Fiber 架构的核心思想,为后续功能扩展奠定了良好的基础。