代码结构如下:

- React.js 作用
- render() : 渲染入口,初始化根 Fiber 并启动调度
- createElement() : 将 JSX 转换为虚拟 DOM 对象
- workLoop() : 调度器,利用 requestIdleCallback 进行时间切片
- performUnitOfWork() : 处理单个 Fiber 工作单元
- commitRoot() : 提交阶段入口
diff
作用:实现一个非常精简的 Fiber 渲染器的核心逻辑(非完整实现,偏教学/探索用途)。
目标与分层:
- render(): 接收虚拟 DOM(由 JSX 或 createElement 创建),将整体渲染任务拆分为 fiber 工作单元并启动时间切片调度。
- performUnitOfWork(): 在 render 阶段构建/链接 fiber 节点并创建真实 DOM(但不立即挂载到页面上,挂载在 commit 阶段统一处理以优化性能)。
- commitRoot()/commitWork(): 将计算完成的 DOM 树统一提交到真实 DOM(避免 render 阶段频繁 DOM 操作)。
重要约定(fiber 结构):
- fiber.type: 节点类型(字符串标签或 'TEXT_ELEMENT')。
- fiber.props: 属性对象(包含 children 数组)。
- fiber.dom: 对应的真实 DOM 节点(在 render 阶段 createDom 后创建)。
- fiber.parent: 父 fiber 引用(用于 commit 时挂载)。
- fiber.child: 第一个子 fiber 链接(深度优先处理顺序)。
- fiber.sibling: 兄弟 fiber 链接(以支持广度上的顺序处理)。
设计说明(简要):
- 为支持页面响应性,渲染任务被拆分为多个小的工作单元(fiber),并通过 requestIdleCallback 的空闲时间片执行。
- render 阶段只负责构建 DOM 节点并建立 fiber 链表(depth-first 构建),真正的 DOM 插入在 commit 阶段一次性完成。
- 目前未
JavaScript
- ReactDom.js 作用:创建根节点
- createRoot() : 创建根节点,提供渲染接口
- fiberUtils.js 作用
- commitWork() : 递归挂载 Fiber 树到真实 DOM
sql
功能:实现 Fiber 提交阶段的操作(commit),把 render 阶段构建的 fiber 树转为真实 DOM 结构。
说明:commit 阶段的核心是把已存在且正确设置 props/dom 的 fiber 节点插入文档中。该模块将提交逻辑单独抽离,
以便让渲染逻辑和提交逻辑职责分离,且更容易进行单元测试。
JavaScript
- domUtils.js
- createDom() : 创建真实 DOM 节点
- updateProperties() : 更新 DOM 属性
- appendDomToRootContainer() : DOM 挂载逻辑
diff
功能概览:
- 本文件包含与真实 DOM 操作相关的纯工具函数,负责在 render/commit 阶段处理 DOM 的创建、属性更新与父容器挂载逻辑。
- 把这些与副作用相关的细节抽象到独立模块,能使主渲染逻辑更简洁且更容易测试。
约束与说明:
- 这些函数会直接操作浏览器 DOM,因此它们的副作用应只在 commit 阶段执行。
- updateProperties 非常基础:仅直接把 props 的字段(非 children)赋值到元素上,未做差分、事件绑定处理或样式合并(这些为后续功能)。
JavaScript
- childrenUtils.js
- normalizeChildren() : 规范化 children 数组
- 处理各种输入类型(undefined、单值、数组)
- 扁平化嵌套数组
- 将文本节点转换为 TEXT_ELEMENT 结构
typescript
功能:规范化 children 列表,返回一个扁平化且统一结构的 children 数组(便于后续构建 fiber)
这个工具做了下列工作:
1) 接受各种可能的 children 输入形态:
- undefined / null
- 单个节点对象(VNode)
- 单个原始值(string / number)
- 数组(可能嵌套)
2) 将 children 规范成一个扁平数组,移除 null/undefined
3) 将原始文本(string / number)转换为一个统一的 TEXT_ELEMENT 虚拟节点,
以便后续的 createDom / props 处理一致
返回值:一个标准化的 children 数组(所有项都为 VNode 样式对象,或整体为空数组)
设计注意点:
- 此处没有深度拷贝 children 对象,因此传入的对象引用会保留。
- 对于非常深/大的 children 数组,flat(Infinity) 可能引发性能问题,生产代码中应使用更高效的迭代器或限制层级。
JavaScript
整体渲染流程

performUnitOfWork 详细流程

commitWork 提交流程

children规范化流程

详细执行流程
阶段一:初始化阶段
- 应用启动
- main.jsx 调用 ReactDom.createRoot(document.getElementById('root'))
- 创建根容器,返回包含 render 方法的对象
- 开始渲染
- 调用 root.render(App)
- 触发 React.render(node, container)
- 设置根 Fiber
- 创建根 Fiber 对象,设置 dom 为容器节点
- props.children 包含要渲染的 App 组件
- 设置 nextWorkOfUnit 为根 Fiber
- 保存 root 引用用于后续提交
阶段二:调度器启动
- 工作循环初始化
- window.requestIdleCallback(workLoop) 启动调度
- workLoop 检查浏览器空闲时间
- 时间切片处理
- 在 deadline.timeRemaining() > 1ms 时持续处理
- 每次循环调用 performUnitOfWork(nextWorkOfUnit)
- 处理完成后更新 nextWorkOfUnit 为返回的下一个 Fiber
阶段三:Render 阶段(Fiber 树构建)
3.1 处理单个 Fiber 单元
- Fiber 类型判断
- 检查是否为函数组件(typeof fiber.type === 'function')
- 函数组件:执行函数获取返回的 VNode,更新 fiber.props.children
- 普通组件:创建 DOM 节点并设置属性
- DOM 节点创建
- 调用 createDom(fiber) 创建对应节点
- TEXT_ELEMENT 创建 TextNode,其他创建 Element
- 调用 updateProperties 设置 DOM 属性
- 子节点规范化
- 调用 normalizeChildren(fiber.props.children)
- 处理各种 children 输入情况
- 扁平化数组,转换文本节点为 TEXT_ELEMENT
- 过滤 null/undefined 节点
- 构建子 Fiber 链表
- 遍历规范化后的 children 数组
- 为每个子节点创建对应的 Fiber
- 建立 parent-child-sibling 链接关系
- 第一个子节点:fiber.child = newFiber
- 后续子节点:prevSibling.sibling = newFiber
3.2 深度优先遍历
- 返回下一个工作单元
- 优先返回子节点:if (fiber.child) return fiber.child
- 其次返回兄弟节点:if (fiber.sibling) return fiber.sibling
- 向上回溯查找父级的兄弟节点
- 遍历完成返回 null
阶段四:Commit 阶段(DOM 挂载)
4.1 提交准备
- 当 nextWorkOfUnit 为 null 且 root 存在时
- 调用 commitRoot() 进入提交阶段
4.2 递归挂载 DOM
- commitWork 执行
- 检查当前 Fiber 是否有 DOM 节点
- 查找最近的有 DOM 的父节点(跳过函数组件)
- 执行 parentFiber.dom.appendChild(fiber.dom)
- 深度优先挂载
- 先递归挂载子节点:commitWork(fiber.child)
- 再递归挂载兄弟节点:commitWork(fiber.sibling)
4.3 完成渲染
- 所有 DOM 节点挂载完成
- 清空 root 引用
- 渲染流程结束
四、关键数据结构
Fiber 节点结构:
javascript
yaml
{
type: 'div' | 'TEXT_ELEMENT' | Function,
props: { children: [], ...attributes },
dom: HTMLElement | Text,
parent: Fiber,
child: Fiber,
sibling: Fiber
}
Plain Text
虚拟 DOM 结构:
javascript
css
// 元素节点
{ type: 'div', props: { id: 'app', children: [...] } }
// 文本节点
{ type: 'TEXT_ELEMENT', props: { nodeValue: 'text', children: [] } }
Plain Text
五、总结
1. 时间切片调度
- 使用 requestIdleCallback 避免阻塞主线程
- 将渲染任务拆分为多个工作单元
- 在浏览器空闲时段执行
2. 阶段分离设计
- Render 阶段:纯计算,构建 Fiber 树,不操作 DOM
- Commit 阶段:批量 DOM 操作,避免频繁重排重绘
3. 链表数据结构
- 使用 child、sibling 指针构建树形结构
- 支持高效的深度优先遍历
- 便于暂停和恢复渲染任务
4. 统一节点处理
- 所有节点(包括文本)都转换为统一结构
- 简化后续处理逻辑
- 支持函数组件和普通组件
5. 模块化设计
- 每个模块职责单一明确
- 工具函数独立,便于测试和维护
- 清晰的依赖关系和数据流向
六、扩展性考虑
当前架构为后续功能扩展提供了良好基础,后续需要做这些:
- Diff 算法:可在 performUnitOfWork 中实现节点比较
- 事件系统:在 updateProperties 中添加事件处理
- Hooks 支持:在函数组件处理时维护状态
- 错误边界:添加异常捕获和处理机制React Fiber 渲染器核心逻辑详解