【React进阶系列第三课】组件函数什么时候被执行

前面说过,React 每次渲染组件,都会执行组件函数。现在,我们来看看,React 会在什么时候渲染组件,也就是什么时候会执行你的组件函数。

组件函数执行的 3 种情况

你的组件函数会在以下 3 种情况下执行:

  1. 组件首次挂载时
  2. 组件的状态或依赖的 Context 发生变化时
  3. 父组件重新渲染时

第 3 种情况,有必要单独说一下。当 React 更新一个组件的时候,会连带者更新它的子组件,即使子组件的 props 没有发生变化。也就是说,React 更新的是整个组件树,而不是单个组件。

有个普遍的错误说法是 "props 变化时,组件会更新"。第一,props 变化,意味着父组件更新了,此时组件更新是因为父组件更新导致的,不管 props 有没有变化,组件都会更新。其次,如果按这个理解,那你说下面这种情况组件会更新吗?

jsx 复制代码
import { createRoot } from "react-dom/client";

let sec = 0;
setInterval(() => {
  sec++;
}, 1000);

function Timer({ secPassed }) {
  return <p>Seconds passed: {secPassed}</p>;
}

createRoot(document.getElementById("root")).render(<Timer secPassed={sec} />);

性能问题

你可能发现了,父组件更新,所有的后代组件也会一起更新,那性能是不是会有问题?尤其是对那些 props 没有变化的后端组件,执行它们不是浪费吗?

从性能上看这确实是一个浪费。不过,实践证明,在大部分项目下,这种性能损耗不会导致明显的性能问题,不然 React 已经被喷惨了。所以,我们平时不用担心性能问题。

不会引发性能问题,很大一个原因是因为大部分的组件函数都很简单,很快就执行完了。一般的组件函数都是做一些变量的声明、读取、赋值,然后创建并返回 React 元素(JSX)。

这也告诉我们,尽量让组件函数简单,把一些具有较高复杂度的计算放在 useMemo 中,避免不必要的计算。

另一个需要关注的是组件的数量,比如:

  • 大量使用某个组件。比如,展示大量数据,每个数据对应一个 <ListItem />
  • 一个组件有大量的后代组件,而且它自身频繁更新。

这类情况,在每次更新时,都会执行大量的组件函数,容易导致性能问题。可以使用 React.memo,当组件的 props 没有变化时,跳过组件的更新。

React.memo

memo 可以让我们在更新前,比较新旧两次的 props,决定要不要执行更新。

js 复制代码
const MemoizedComponent = memo(SomeComponent, arePropsEqual?)

默认情况下,是对每个 prop 做浅比较,如果存在对象结构的 prop,一般使用 lodash.isEqual 来做深比较,如果含有函数,还需要注意保证先后两次函数的引用不发生变化,不然即使 lodash.isEqual 也会返回 false

memo 优化的是第 3 种情况,也就是父组件更新都会导致子组件更新的情况。对于第 1、2 种情况,组件更新是不能也不可避免的。

memo 可以提升性能,但是,不要滥用,除非你有理由认为这个组件的频繁更新会导致性能问题。

最后,应该在组件使用的时候,才决定要不要使用 memo,不要在组件定义的时候就使用 memo。假如你对外提供组件,你不需要在导出之前使用 memo。因为你写组件的时候,不知道父组件会不会频繁更新,也不知道组件会不会被大量使用,也不知道如何有效比较两次 props,这些只有使用的时候才知道。

总结

  • 函数组件会在 3 中情况下执行:
    1. 组件首次挂载时
    2. 组件的状态或依赖的 Context 发生变化时
    3. 父组件重新渲染时
  • 父组件更新时,子组件默认都会更新,即使子组件的 props 没有变化。
  • 这种冗余的更新,在大部分情况下,不会导致明显的性能问题。
  • 为了避免性能问题,记住:
    • 尽量保持组件简单。
    • 在组件内如果有高复杂度的计算,使用 useMemo
    • 对于大量使用的组件,或者更新繁重的组件,可以在必要时使用 React.memo
  • 组件自身的状态或依赖的 Context 变化时,组件一定会更新,React.memo 不会影响这种情况。
相关推荐
帧栈几秒前
开发避坑指南(27):Vue3中高效安全修改列表元素属性的方法
前端·vue.js
max5006004 分钟前
基于桥梁三维模型的无人机检测路径规划系统设计与实现
前端·javascript·python·算法·无人机·easyui
excel19 分钟前
使用函数式封装绘制科赫雪花(Koch Snowflake)
前端
萌萌哒草头将军1 小时前
Node.js v24.6.0 新功能速览 🚀🚀🚀
前端·javascript·node.js
持久的棒棒君3 小时前
启动electron桌面项目控制台输出中文时乱码解决
前端·javascript·electron
小离a_a3 小时前
使用原生css实现word目录样式,标题后面的...动态长度并始终在标题后方(生成点线)
前端·css
郭优秀的笔记4 小时前
抽奖程序web程序
前端·css·css3
布兰妮甜4 小时前
CSS Houdini 与 React 19 调度器:打造极致流畅的网页体验
前端·css·react.js·houdini
小小愿望4 小时前
ECharts 实战技巧:揭秘 X 轴末项标签 “莫名加粗” 之谜及破解之道
前端·echarts
小小愿望5 小时前
移动端浏览器中设置 100vh 却出现滚动条?
前端·javascript·css