⚡ 30 秒速记
- 先定位昂贵组件和交互,不追求零渲染
- 状态尽量靠近使用处,避免无关范围更新
memo控属性更新边界,缓存必须有稳定依赖- 大列表减少实际渲染节点
- 自定义比较成本与正确性都要验证
React 性能优化先找哪次更新让用户等,再减少那次更新里不必要的工作。 状态放得过高会扩大影响范围,长列表可能需要虚拟化,昂贵计算才值得缓存。memo 能帮助跳过属性未变的重复渲染,但自身状态和上下文仍会更新;引用每次都新建时,缓存也难以命中。改完用真实交互复测,避免比较函数比重新渲染还贵。
版本校准: 本文若分析 ReactDOM.render、旧生命周期或栈调和,应把它视为理解架构演进的历史路径。React 19 已移除 ReactDOM.render,当前客户端入口使用 createRoot;并发渲染也必须区分可中断的渲染阶段与同步提交阶段。迁移前对照 React 19 官方升级指南。
- 使用
shouldComponentUpdate规避冗余的更新逻辑 PureComponent + Immutable.jsReact.memo与useMemo
注:这 3 个思路同时也是 React 面试中“性能优化”这一环的核心所在
# 善用 shouldComponentUpdate
shouldComponentUpdate 的调用形式如下:
shouldComponentUpdate(nextProps, nextState)
render方法由于伴随着对虚拟DOM的构建和对比,过程可以说相当耗时。而在React当中,很多时候我们会不经意间就频繁地调用了render。为了避免不必要的render操作带来的性能开销,React 提供了shouldComponentUpdate这个口子。React 组件会根据shouldComponentUpdate的返回值,来决定是否执行该方法之后的生命周期,进而决定是否对组件进行re-render(重渲染)。shouldComponentUpdate的默认值为true,也就是说 “无条件 re-render”。在实际的开发中,我们往往通过手动往shouldComponentUpdate中填充判定逻辑,来实现“有条件的 re-render”。
首先我们来看两个子组件的代码,这里为了尽量简化与数据变更无关的逻辑,ChildA 和 ChildB 都只负责从父组件处读取数据并渲染,它们的编码分别如下所示。
// ChildA.js:
import React from "react";
export default class ChildA extends React.Component {
render() {
console.log("ChildA 的render方法执行了");
return (
<div className="childA">
子组件A的内容:
{this.props.text}
</div>
);
}
}