🧑💻 前言
在上一篇文章里,我们从 scheduleUpdateOnFiber 出发,跟踪到 React 内部如何触发 调度更新。
调度完成后,更新会进入 render 阶段。
render 阶段的核心目标是:基于最新的 props、state,构建一棵全新的 Fiber 树,并在完成阶段生成 DOM 节点。
本篇文章我们将从 首次挂载流程 出发,结合源码逐步拆解,理解 React 在没有 Diff 复用的情况下,如何一步步"生造"出一棵 Fiber 树并对应的 DOM。
1. Render 阶段全景
先整体感受一下 render 阶段的 流程图:
            
            
              ts
              
              
            
          
          Render 流程:
│
├─ 初始化阶段
│  ├─ ReactDOM.render(<App />, container)
│  ├─ 创建 FiberRoot 与 HostRootFiber
│  └─ scheduleUpdateOnFiber(HostRootFiber)
│
├─ 递阶段(beginWork)
│  ├─ performUnitOfWork(workInProgress)
│  ├─ beginWork(current, workInProgress)
│  │   ├─ HostRoot → updateHostRoot()
│  │   ├─ ClassComponent → constructClassInstance()
│  │   ├─ FunctionComponent → renderWithHooks()
│  │   └─ HostComponent → 创建子Fiber
│  └─ 向下遍历子节点
│
├─ 归阶段(completeUnitOfWork)
│  ├─ completeWork(current, workInProgress)
│  │   ├─ HostComponent → createInstance()
│  │   ├─ HostText → createTextInstance()
│  │   ├─ appendAllChildren()
│  │   └─ finalizeInitialChildren()
│  └─ 向上回溯父节点
│
└─ Fiber 树构建完成
   └─ 等待 commit 阶段插入 DOM一句话总结:Render = beginWork 向下递 + completeWork 向上归。
2. 初始化阶段
当我们调用:
            
            
              ts
              
              
            
          
          root.render(<App />, document.getElementById('root'))流程是这样的:
- 创建 FiberRoot,内部保存容器信息(containerInfo = #root)。
- 创建 HostRootFiber,作为 Fiber 树的根。
- 将 <App />作为一个 update 放进HostRootFiber.updateQueue。
- 调度更新 → scheduleUpdateOnFiber(HostRootFiber)。
此时,调度器会在合适的时机执行 render 阶段。
3. beginWork ------ 递阶段
render 阶段从 根节点(HostRootFiber) 出发,调用 performUnitOfWork:
            
            
              js
              
              
            
          
          function performUnitOfWork(unitOfWork) {
  // 1. 进入递阶段
  const next = beginWork(unitOfWork.alternate, unitOfWork, renderLanes);
  // 2. 保存 props
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  // 3. 返回子节点(如果有)
  return next;
}3.1 HostRoot → updateHostRoot
            
            
              js
              
              
            
          
          function updateHostRoot(current, workInProgress, renderLanes) {
  // 处理 updateQueue,拿到 element
  processUpdateQueue(workInProgress, nextProps, null, renderLanes);
  const nextState = workInProgress.memoizedState;
  const nextChildren = nextState.element; // <App />
  // 协调子节点,创建 AppFiber
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child; // 返回 AppFiber
}首次挂载时:
- current 为 null。
- reconcileChildren内部调用- mountChildFibers,直接创建新 Fiber,不做 diff。
3.2 ClassComponent → constructClassInstance
            
            
              js
              
              
            
          
          function updateClassComponent(current, workInProgress, Component, nextProps) {
  if (workInProgress.stateNode === null) {
    // 🔥 首次挂载:创建实例
    constructClassInstance(workInProgress, Component, nextProps);
    mountClassInstance(workInProgress, Component, nextProps);
  }
  // 调用 render() 得到 children
  const nextChildren = workInProgress.stateNode.render();
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}特点:
- new Component(props)创建实例。
- 执行 render(),得到 React 元素树。
- 继续递归子节点。
3.3 FunctionComponent → renderWithHooks
            
            
              js
              
              
            
          
          function updateFunctionComponent(current, workInProgress, Component, nextProps) {
  // 首次挂载:设置 Hook 调度器
  let nextChildren = renderWithHooks(current, workInProgress, Component, nextProps);
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}特点:
- 使用 HooksDispatcherOnMount。
- 执行函数组件 Component(nextProps)。
- 构建 Hook 链表(useState、useEffect)。
3.4 HostComponent → DOM 容器
            
            
              js
              
              
            
          
          function updateHostComponent(current, workInProgress, renderLanes) {
  const nextProps = workInProgress.pendingProps;
  const nextChildren = nextProps.children;
  // 协调子节点
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}特点:
- Fiber 本身没有立即创建 DOM。
- 只负责生成子 Fiber(例如 div下的子节点)。
4. completeUnitOfWork ------ 归阶段
递归到最深的子节点后,进入 归阶段:
            
            
              js
              
              
            
          
          function completeUnitOfWork(unitOfWork) {
  do {
    const current = unitOfWork.alternate;
    const returnFiber = unitOfWork.return;
    // 调用 completeWork
    completeWork(current, unitOfWork, subtreeRenderLanes);
    // 如果有兄弟,切换到兄弟
    if (unitOfWork.sibling !== null) {
      workInProgress = unitOfWork.sibling;
      return;
    }
    // 否则回到父节点
    unitOfWork = returnFiber;
    workInProgress = unitOfWork;
  } while (unitOfWork !== null);
}5. completeWork ------ 创建 DOM
核心逻辑:
            
            
              js
              
              
            
          
          function completeWork(current, workInProgress, renderLanes) {
  switch (workInProgress.tag) {
    case HostComponent: {
      if (current === null) {
        // 🔥 首次挂载:创建 DOM
        const instance = createInstance(type, newProps, rootContainer, context);
        appendAllChildren(instance, workInProgress);
        finalizeInitialChildren(instance, type, newProps, rootContainer, context);
        workInProgress.stateNode = instance;
      }
      return null;
    }
    case HostText: {
      if (current === null) {
        workInProgress.stateNode = createTextInstance(newProps);
      }
      return null;
    }
    // 组件类节点(ClassComponent、FunctionComponent)不创建 DOM
  }
}执行要点:
- createInstance → document.createElement(type)。
- appendAllChildren → 遍历子 Fiber,找到 HostComponent/HostText,appendChild。
- finalizeInitialChildren → 设置属性、事件绑定。
- 保存到 fiber.stateNode。
6. 流程小结
以一个例子:
            
            
              js
              
              
            
          
          function App() {
  return <div className="app"><p>Hello</p></div>;
}
ReactDOM.render(<App />, root);执行过程:
- HostRoot → 创建 AppFiber。
- AppFiber → 执行 App()→ 返回<div>。
- divFiber → 创建 pFiber。
- pFiber → 创建 TextFiber。
- 归阶段 → 从 TextFiber 开始,逐层 createTextNode → createElement → appendChild。
- 最终,Fiber 树构建完成,DOM 树也就绪。
📌 总结
React Render 首次挂载流程:
- 初始化:创建 FiberRoot 和 HostRootFiber,调度更新。
- 递阶段(beginWork) :向下遍历,基于组件类型创建子 Fiber。
- 归阶段(completeWork) :向上回溯,创建 DOM 实例,收集副作用。
- 完成 Fiber 树:等待 commit 阶段将 DOM 插入页面。
一句话总结:
👉 首次挂载时,render 阶段会完整走一遍 Fiber 树递归,创建所有 Fiber 和 DOM 实例,不存在 Diff 复用。