从0到1实现react(一):架构设计与初次渲染流程

223 阅读4分钟

🐭

鼠鼠准备开始从0到1开始实现react,加深对源码的理解

简单实现了下react初次渲染,代码版本:github.com/zhuxin0/min…

核心模块

1. React 层 (react.js)

  • 导出基础组件类 Component
  • 导出 Hooks:useReduceruseState
  • 作为用户 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 架构的核心思想,为后续功能扩展奠定了良好的基础。