渲染与提交
在组件显示到屏幕上之前,它们必须由 React 进行渲染。理解这个过程将帮助你思考代码的执行方式并解释其行为。
您将学习
- 在 React 中渲染的含义
- React 何时以及为何渲染组件
- 在屏幕上显示组件所涉及的步骤
- 为什么渲染不总是产生 DOM 更新
想象一下,你的组件是厨房里的厨师,用食材组装美味的菜肴。在这个场景中,React 是服务员,接收顾客的订单并将其送达。这个请求和提供 UI 的过程分为三个步骤:
- 触发渲染(将客人的订单送到厨房)
- 渲染组件(在厨房准备订单)
- 提交到 DOM(将订单放到桌上)
触发
渲染
提交
插图作者:Rachel Lee Nabors
步骤 1:触发渲染
组件渲染有两个原因:
- 这是组件的初始渲染。
- 组件(或其某个祖先)的状态已更新。
初始渲染
当你的应用启动时,你需要触发初始渲染。框架和沙箱有时会隐藏这段代码,但它是通过调用createRoot并传入目标 DOM 节点,然后调用其render方法并传入你的组件来完成的:
尝试注释掉root.render()调用,看看组件消失!
状态更新时的重新渲染
组件完成初始渲染后,你可以通过使用set 函数更新其状态来触发进一步的渲染。更新组件的状态会自动将渲染加入队列。(你可以想象这就像餐厅客人在下了第一份订单后,根据他们的口渴或饥饿状态,又点了茶、甜点和各种东西。)
状态更新...
...触发...
...渲染!
插图作者:Rachel Lee Nabors
步骤 2:React 渲染你的组件
在你触发渲染后,React 会调用你的组件以确定屏幕上应显示什么。“渲染”就是 React 调用你的组件。
- 在初始渲染时,React 会调用根组件。
- 对于后续渲染,React 会调用其状态更新触发了渲染的函数组件。
这个过程是递归的:如果更新后的组件返回了其他组件,React 接下来会渲染那个组件,如果该组件也返回了某些内容,它会接着渲染那个组件,依此类推。这个过程将持续到没有更多嵌套组件,并且 React 确切知道屏幕上应显示什么为止。
在下面的示例中,React 将多次调用Gallery()和Image():
- 在初始渲染期间,React 会创建 DOM 节点,用于
<section>、<h1>和三个<img>标签。 - 在重新渲染期间,React 会计算自上次渲染以来,它们的哪些属性(如果有的话)发生了变化。在进入下一步(提交阶段)之前,它不会对这些信息进行任何操作。
陷阱
渲染必须始终是纯计算:
- 相同的输入,相同的输出。给定相同的输入,组件应始终返回相同的 JSX。(当有人点了带番茄的沙拉时,他们不应该收到带洋葱的沙拉!)
- 它只关心自己的事。它不应更改渲染前已存在的任何对象或变量。(一个订单不应更改其他人的订单。)
否则,随着代码库复杂性的增长,你可能会遇到令人困惑的 bug 和不可预测的行为。在“严格模式”下开发时,React 会调用每个组件的函数两次,这有助于发现由不纯函数引起的错误。
步骤 3:React 将更改提交到 DOM
在渲染(调用)你的组件之后,React 将修改 DOM。
- 对于初始渲染,React 将使用appendChild()DOM API 将其创建的所有 DOM 节点放到屏幕上。
- 对于重新渲染,React 将应用必要的最小操作(在渲染时计算!)以使 DOM 与最新的渲染输出匹配。
React 仅在两次渲染之间存在差异时才会更改 DOM 节点。例如,这里有一个组件,它每秒都会从其父组件接收不同的 props 并重新渲染。请注意,你可以向<input>添加一些文本,更新其value,但当组件重新渲染时,文本不会消失:
这是因为在最后一步中,React 仅使用新的<h1>更新time的内容。它发现<input>在 JSX 中出现的位置与上次相同,因此 React 不会触碰<input>——或其value!
尾声:浏览器绘制
渲染完成且 React 更新了 DOM 之后,浏览器将重新绘制屏幕。尽管这个过程被称为“浏览器渲染”,但为了避免在文档中产生混淆,我们将其称为“绘制”。

插图作者:Rachel Lee Nabors
回顾
- React 应用中的任何屏幕更新都分三步进行:
- 触发
- 渲染
- 提交
- 你可以使用严格模式来发现组件中的错误
- 如果渲染结果与上次相同,React 不会操作 DOM
