在你的组件显示在屏幕上之前,它们必须由 React 渲染。理解这个过程中的步骤将帮助你思考你的代码是如何执行的,并解释它的行为。
你将学习
- React 中渲染的含义
- React 何时以及为何渲染组件
- 在屏幕上显示组件所涉及的步骤
- 为什么渲染并不总是产生 DOM 更新
想象一下,你的组件是厨房里的厨师,从食材中组装美味的菜肴。在这个场景中,React 是服务员,他接收顾客的订单并将订单送到他们手中。这个请求和服务 UI 的过程包含三个步骤
- 触发渲染(将客人的订单送到厨房)
- 渲染组件(在厨房准备订单)
- 提交到 DOM(将订单放在桌子上)
步骤 1:触发渲染
组件渲染有两个原因
- 这是组件的初始渲染。
- 组件(或其祖先之一)的状态已更新。
初始渲染
当你的应用程序启动时,你需要触发初始渲染。框架和沙箱有时会隐藏此代码,但它是通过使用目标 DOM 节点调用createRoot
,然后使用你的组件调用其render
方法来完成的
import Image from './Image.js'; import { createRoot } from 'react-dom/client'; const root = createRoot(document.getElementById('root')) root.render(<Image />);
尝试注释掉root.render()
调用,看看组件是否消失了!
状态更新时重新渲染
组件初始渲染后,你可以使用set
函数更新其状态来触发进一步的渲染。更新组件的状态会自动排队渲染。(你可以想象这些就像餐厅客人下单后,根据他们口渴或饥饿的状态,再点茶、甜点和各种东西一样。)
步骤 2:React 渲染你的组件
触发渲染后,React 会调用你的组件以确定在屏幕上显示什么。“渲染”是指 React 调用你的组件。
- 在初始渲染中,React 将调用根组件。
- 对于后续渲染,React 将调用其状态更新触发渲染的函数组件。
此过程是递归的:如果更新的组件返回其他组件,React 将接下来渲染_该_组件;如果该组件也返回某些内容,它将接下来渲染_该_组件,依此类推。此过程将继续进行,直到没有更多嵌套组件,并且 React 确切知道应该在屏幕上显示什么。
在以下示例中,React 将多次调用Gallery()
和Image()
export default function Gallery() { return ( <section> <h1>Inspiring Sculptures</h1> <Image /> <Image /> <Image /> </section> ); } function Image() { return ( <img src="https://i.imgur.com/ZF6s192.jpg" alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals" /> ); }
- 在初始渲染期间,React 将会创建DOM节点 用于
<section>
,<h1>
,以及三个<img>
标签。 - 在重新渲染期间,React 将会计算自上次渲染以来,哪些属性(如果有)发生了更改。在进入下一步——提交阶段之前,它不会对这些信息做任何处理。
步骤 3:React 将更改提交到 DOM
渲染(调用)您的组件后,React 将修改 DOM。
- 对于初始渲染,React 将使用
appendChild()
DOM API 将其创建的所有 DOM 节点放到屏幕上。 - 对于重新渲染,React 将应用最少的必要操作(在渲染时计算!)以使 DOM 与最新的渲染输出匹配。
只有当渲染结果之间存在差异时,React 才会更改 DOM 节点。例如,这是一个组件,它每秒都会通过其父组件传递不同的 props 来重新渲染。请注意,您可以向<input>
中添加一些文本,更新其value
,但当组件重新渲染时,文本不会消失。
export default function Clock({ time }) { return ( <> <h1>{time}</h1> <input /> </> ); }