🧑💻 写在开头
点赞 + 收藏 = 学会 🤣
在上一篇文章里,我们讲了 React 的三大全局对象,以及 React 是如何启动的。但是你可能会问:
React 启动好了,怎么把
<App />
这个组件挂上页面呢?
这篇文章就带你从源码角度,分析 root.render
到 <App />
挂载到 DOM 的完整原理。
🔎 高层回顾:React 启动 + 渲染 App 的总体脉络
React 应用中,渲染 <App />
大致分为几步:
-
入口调用
javascriptReactDOM.createRoot(document.getElementById('root')).render(<App />); // 或 ReactDOM.render(<App />, document.getElementById('root'));
也就是告诉 React:"把
<App />
挂到这个 DOM 容器里。" -
创建根 Fiber
React 内部会创建一个 FiberRoot / HostRootFiber 节点,作为整棵 Fiber 树的根。
containerInfo
指向div#root
DOM 容器stateNode
或相关属性标记宿主环境
-
构建 Fiber 树
- React 从根 Fiber 出发,创建
<App />
的 Fiber 节点 - 递归构建子组件 Fiber,直到叶子 DOM 节点
- React 从根 Fiber 出发,创建
-
Diff & Reconcile
- React 对比新旧 Fiber 树
- 决定 DOM 插入、更新、删除操作
-
Commit 阶段
- 真正把
<App />
对应的 DOM 插入到div#root
上
- 真正把
🏗 App 是怎样插入的?
关键对象:
ReactDOMRoot
FiberNode
/HostRootFiber
containerInfo
指向页面 DOM<App />
组件本身
流程:
-
入口调用 render
jsReactDOM.createRoot(root).render(<App />);
-
React 创建根 Fiber
HostRootFiber
节点作为 Fiber 树根containerInfo
指向div#root
-
React 启动调度 / 渲染机制
<App />
被作为 root 元素创建 Fiber 节点- JSX 子树继续构建 Fiber,直到叶子节点
-
DOM 操作应用
- 所有 DOM 操作作用在
containerInfo
上
- 所有 DOM 操作作用在
-
最终渲染完成
<App />
对应的 DOM 挂载到div#root
⚙ render 函数源码实现
1. ReactDOM.render (传统 API)
js
export function render(element, container, callback) {
if (!isValidContainerLegacy(container)) {
throw new Error('Target container is not a DOM element.');
}
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
- 核心逻辑:调用
legacyRenderSubtreeIntoContainer
,把元素渲染到 DOM 容器
2. ReactDOMRoot.render (新 API)
js
ReactDOMRoot.prototype.render = function(children) {
const root = this._internalRoot;
if (root === null) throw new Error('Cannot update an unmounted root.');
updateContainer(children, root, null, null);
};
- 核心:把用户传入的
<App />
交给updateContainer
处理 _internalRoot
就是 FiberRoot 对象
🔑 updateContainer 的原理
updateContainer
是将用户 JSX 转换为 React 内部更新机制的桥梁。
函数签名
js
export function updateContainer(
element, // 要渲染的 JSX 元素
container, // Fiber 根节点
parentComponent,
callback
) { ... }
工作流程
- 获取当前 Fiber / 时间 / 优先级
js
const current = container.current;
const eventTime = requestEventTime();
const lane = requestUpdateLane(current);
- 获取根 Fiber
- 事件时间戳
- 确定更新优先级(Lane)
- 处理 Context
js
const context = getContextForSubtree(parentComponent);
if (!container.context) container.context = context;
else container.pendingContext = context;
- 为子树传递 Context
- 创建 Update 对象
js
const update = createUpdate(eventTime, lane);
update.payload = { element };
if (callback) update.callback = callback;
-
将 JSX 包装为内部 Update 对象
-
包含:
eventTime
、lane
、payload
、callback
- 加入更新队列并调度
js
const root = enqueueUpdate(current, update, lane);
if (root !== null) {
scheduleUpdateOnFiber(root, current, lane, eventTime);
entangleTransitions(root, current, lane);
}
enqueueUpdate
:加入 Fiber 的更新队列scheduleUpdateOnFiber
:开始调度渲染entangleTransitions
:处理 Transition 优先级纠缠
🔄 渲染流程总结
js
用户调用 root.render(<App />)
↓
updateContainer
↓
enqueueUpdate (加入更新队列)
↓
scheduleUpdateOnFiber (触发调度)
↓
renderRootSync/Concurrent (构建 Fiber 树)
↓
commitRoot (提交到 DOM)
✅ 总结
-
root.render(<App />)
的本质是:- 将用户 JSX 转换成 Fiber 更新对象
- 挂到根 Fiber 上
- 启动调度 / 渲染 / commit 流程
-
updateContainer
是 render 的关键桥梁:- 处理 Context
- 创建 Update
- 入队并触发调度
-
Fiber 树最终构建完成后,DOM 被插入
containerInfo
指向的真实容器
一句话概括:
render = JSX → Update → Fiber 更新队列 → 调度 → render → commit → DOM
后序
本篇已经到了scheduleUpdateOnFiber函数进入调和,也就是react-reconciler的工作,下一篇我们将讲解react的render流程,下图来自图解react
