渲染和提交
在您的组件显示在屏幕上之前,它们必须由 React 渲染。 了解此过程中的步骤将帮助您思考代码的执行方式并解释其行为。
你将学习
- React 中的渲染意味着什么
- React 何时以及为何渲染组件
- 在屏幕上显示组件所涉及的步骤
- 为什么渲染并不总是产生 DOM 更新
想象一下,您的组件是厨房里的厨师,用食材烹制美味佳肴。 在这个场景中,React 是一个服务员,负责接收客户的请求并为他们带来订单。 这个请求和提供 UI 的过程分为三个步骤:
- 触发渲染(将客人的订单送到厨房)
- 渲染组件(在厨房准备订单)
- 提交给 DOM(在表格上下订单)
第1步:触发渲染
组件渲染的原因有两个:
这是组件的初始渲染。
组件(或其祖先之一)的状态已更新。
初始渲染
当您的应用程序启动时,您需要触发初始渲染。 框架和沙箱有时会隐藏此代码,但它是通过使用目标 DOM 节点调用 createRoot,然后使用您的组件调用其render方法来完成的:
Image.js
export default 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"
/>
);
}
index.js
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():
index.js
import Gallery from './Gallery.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Gallery />);
Gallery.js
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 将为 <section>、<h1> 和三个 <img> 标签创建 DOM 节点。
- 在重新渲染期间,React 将计算它们的哪些属性(如果有)自上次渲染以来发生了变化。 在下一步,即提交阶段之前,它不会对这些信息做任何事情。
"缺陷
渲染必须始终是纯计算:
- 相同的输入,相同的输出。 给定相同的输入,组件应该始终返回相同的 JSX。 (当有人点西红柿沙拉时,他们不应该点洋葱沙拉!)
- 它只管自己的事。 它不应更改渲染前存在的任何对象或变量。 (一个订单不应更改任何其他人的订单。)
否则,随着代码库变得越来越复杂,您可能会遇到令人困惑的错误和不可预知的行为。 在“严格模式”下开发时,React 会调用每个组件的函数两次,这可以帮助暴露因函数不纯而导致的错误。
深度阅读:性能优化
如果更新组件在树中的位置非常深,则渲染嵌套在更新组件中的所有组件的默认行为对于性能来说并不是最佳的。 如果遇到性能问题,性能部分中描述了几种选择加入的方法来解决它。 不要过早优化!
第3步:React提交变更到DOM
在渲染(调用)你的组件后,React 将修改 DOM。
- 对于初始渲染,React 将使用 appendChild() DOM API 将它创建的所有 DOM 节点放在屏幕上。
- 对于重新渲染,React 将应用最少的必要操作(在渲染时计算!)以使 DOM 匹配最新的渲染输出。
如果渲染之间存在差异,React 只会更改 DOM 节点。 例如,这里有一个组件每秒使用从其父级传递的不同props重新渲染。 注意如何将一些文本添加到 <input> 中,更新它的值,但是当组件重新渲染时文本不会消失:
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input />
</>
);
}
这是可行的,因为在最后一步中,React 仅使用新时间更新 <h1> 的内容。 它看到 <input> 出现在 JSX 中与上次相同的位置,因此 React 不会触及 <input> — 或其值!
终章:浏览器绘制
渲染完成并且 React 更新 DOM 后,浏览器将重新绘制屏幕。 虽然这个过程被称为“浏览器渲染”,但我们将其称为“ painting”以避免在这些文档的其余部分中混淆。
回顾
- React 应用程序中的任何屏幕更新都分三步进行:
- 触发
- 渲染
- 提交
- 您可以使用严格模式查找组件中的错误
- 如果渲染结果与上次相同,React 不会触碰DOM
网友评论