【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 不会影响这种情况。
相关推荐
前端小白从0开始24 分钟前
Vue3项目实现WPS文件预览和内容回填功能
前端·javascript·vue.js·html5·wps·文档回填·文档在线预览
難釋懷1 小时前
Vue解决开发环境 Ajax 跨域问题
前端·vue.js·ajax
特立独行的猫a1 小时前
Nuxt.js 中的路由配置详解
开发语言·前端·javascript·路由·nuxt·nuxtjs
咸虾米1 小时前
在uniCloud云对象中定义dbJQL的便捷方法
前端·javascript
梨子同志1 小时前
JavaScript Proxy 和 Reflect
前端·javascript
汤圆炒橘子1 小时前
状态策略模式的优势分析
前端
90后的晨仔2 小时前
解析鸿蒙 ArkTS 中的 Union 类型与 TypeAliases类型
前端·harmonyos
IT_陈寒2 小时前
Element Plus 2.10.0 重磅发布!新增Splitter组件
前端·人工智能·后端
挑战者6668882 小时前
vue入门环境搭建及demo运行
前端·javascript·vue.js
贩卖纯净水.2 小时前
Webpack的基本使用 - babel
前端·webpack·node.js