root.render(<App />)之后 React 干了哪些事?

root.render(<App />) 之后 React 到底干了哪些事、屏幕上的像素是怎么出来的、以及以后 JSX 一改页面又是如何"自动"刷新的?

一、第一次 render 之后发生了什么

  1. 创建 Fiber 根节点
    createRoot(domContainer) 会在内部生成一个 FiberRoot 对象,它记住"真实 DOM 入口"和"React 世界"的对应关系。

  2. 进入 concurrent 调度循环
    root.render(<App />) 并不会立即把 DOM 写出来,而是向 Scheduler(调度器)提交一个"更新任务"(update)。

    Scheduler 依据浏览器每一帧的空闲时间(requestIdleCallback / MessageChannel polyfill)决定"现在能不能干一点活",从而保证 60 fps 不卡帧。

  3. 生成/对比 Fiber 树(render 阶段,可中断)

    • 从根 Fiber 开始,React 深度优先遍历新返回的 JSX 元素,为每个组件/节点创建或复用对应的 Fiber 节点
    • 这个阶段完全不碰真实 DOM ,只做两件事:
      ① 调用函数组件(或 class 组件的 render)拿到 JSX → 生成子 Fiber;
      ② 在遍历过程中把"需要做的 DOM 操作"记录成一条 effectList(也叫"副作用链")。
    • 因为可中断,如果浏览器忽然要处理用户输入,React 可以把工作切片保存,先让出线程,稍后再继续。
  4. 提交阶段(commit,不可中断)

    当整棵 Fiber 树都 diff 完,React 进入同步的 commit:

    a) before mutation → 调用 getSnapshotBeforeUpdate(class 组件遗留)。

    b) mutation → 真正操作 DOM:新增、删除、移动节点,设置 textContentstyle、事件监听器......

    c) layout → 调用 useLayoutEffect / componentDidMount / componentDidUpdate;此时 DOM 已经落地,但浏览器还没 paint,适合测量 DOM 尺寸。

    d) 浏览器执行 paint ,像素出现在屏幕。

    e) passive → 异步调度 useEffect 回调(下一帧后执行)。

二、为什么能看到页面

commit 阶段的 mutation 一步直接把 DOM 增删改做完,浏览器紧跟其后执行重排/重绘,于是用户就看见了初次界面。

三、后续 JSX 改动如何更新视图

  1. 触发更新

    • 调用 setState / useState 的 setter / forceUpdate / root.render 新 JSX ... 都会生成一个"更新对象",追加到对应 Fiber 节点的队列里。
  2. 再次走"调度 → render(diff)→ commit"循环

    • 调度器依旧按优先级(用户阻塞级、普通、空闲、离线)安排任务。

    • render 阶段把"新 JSX"与"上次留下的 Fiber 树"做 对比(diff)

    • 产生的 effectList 里这次可能只有"更新某文本节点""给某个 div 加新 class"等少量操作。

    • commit 阶段再次同步执行这些 DOM 操作 → 浏览器 paint → 用户看到刷新后界面。

  3. 函数组件的"重新执行"

    函数组件在每一次 render 阶段都会被整体重新调用一次 ,Hooks 靠存在 Fiber 节点的 memorizedState 链表来保持状态,因此你写在函数体里的代码每次都会跑,但状态不会丢失。

相关推荐
NiceCloud喜云3 小时前
Opus 4.8 的 Effort Control 怎么选:Low 到 Max 五档策略
android·java·大数据·前端·c++·python·spring
wordbaby4 小时前
React Native + RNOH:跨页面数据回传的最佳实践与避坑指南
前端·react native
GISer_Jing4 小时前
Three.js着色器编译机制深度解析
javascript·webgl·着色器
丷丩4 小时前
MapLibre GL JS第22课:查看本地GeoJSON
前端·javascript·map·mapbox·maplibre gl js
jiayong234 小时前
面试中遇到不熟悉问题的应对策略深度解析
面试·职场和发展
油炸自行车4 小时前
Claude Code 错误:API Error: 400 Failed to deserialize the JSON body into the
开发语言·javascript·json·trae·claude code·api error 400
Front思5 小时前
AI前端工程师需要具备能力+
前端·人工智能·ai
JAVA社区6 小时前
Java高级全套教程(十)—— SpringCloudAlibaba超详细实战详解
java·开发语言·spring cloud·面试·职场和发展
ZC跨境爬虫7 小时前
跟着 MDN 学CSS day_29:(掌握文本与字体样式的核心艺术)
前端·css·ui·html·tensorflow
李子琪。8 小时前
网络空间安全深度实战:CSRF 漏洞原理剖析与基于 Token 的纵深防御体系构建(全栈实验报告)
前端·安全·csrf