首页 / 文章

90%的React开发者以错误方式优化重渲染

大多数 React 开发者以错误的方式优化组件重新渲染。本文将揭示 React 性能问题的真正根源及有效的解决方案,并提供相应的实施检查清单。

发布者:REACTAPP.TOP阅读约 13 分钟2912 词

来源: 90% of React Developers Optimize Re-Renders Incorrectly — Here’s What Actually Works

以下内容围绕“90%的React开发者错误地优化了重新渲染——真正有效的方法是什么”这一主题,提供了一条实用路径。重点在于契约、检查机制以及可直接插入的代码占位符,而非激励性表述。 在完成概览阶段时,首先明确契约内容:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功检查标准,并杜绝无声的半完成状态。

真正的问题:我们不明白React何时会重新渲染

将“The Real Problem We stage”视为可测量的对象来处理时,其效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,就能避免在从演示环境过渡到共享环境时出现意外账单。尽量保持渲染工作的成本较低,只有在经过测量之后才将高成本的推导操作放在记忆化机制之后处理。过早使用记忆化可能会掩盖过时的属性错误。

你终于打开 React Profiler 的那一天

《The Day you Finally stage》这一概念最好被视作一个可衡量的指标。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 将配置置于应用程序代码之外。环境文件、密钥存储和功能开关应集中存放,这样操作人员无需查看整个系统结构即可进行审计。 尽量降低渲染成本,只有在经过测量后才会将耗时的计算操作放入缓存机制中。过早使用缓存可能会掩盖过时属性带来的错误。

核心真理:重新渲染并非敌人,无谓的重新渲染才是

将“核心真相重新渲染”阶段视为可度量的对象来处理,效果最佳。在扩大范围之前,先记录一个理想案例、一个失败案例以及回滚说明。 同时记录正常流程与恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续需要补充的功能。 尽量降低渲染成本,只有在经过测量后才考虑通过记忆化手段处理高成本的推导过程。过早使用记忆化可能会掩盖过时的属性错误。 将“核心真相重新渲染”阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声的半完成状态。

第1节:最终让React变得易用的思维模型

在“第1阶段:思维阶段”中,应在修改代码之前明确输入内容、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间以及令牌或查询成本。提前显示成本信息,可避免在从演示环境切换到共享环境时出现意外费用。当下一步操作是编写代码或调用工具时,应优先选择具有架构验证的结构化输出,而非自由形式的文字描述。

React = 函数执行引擎

在 React 函数执行引擎阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一个位置,这样操作人员无需查看整个架构即可进行审计。 状态应与负责数据变更的组件放在一起。将所有数据都放到全局存储中会使得时序错误更难被发现。

Component(props) → JSX

第2节:为何你的组件重绘次数比你想象的多

在第二部分“为何需要此阶段”中,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。 状态应与负责数据变更的组件放在一起管理。将所有数据都存放在全局存储中会使得时序相关的问题更难被发现。 在第二部分“为何需要此阶段”中,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与经过验证的输出之间的契约。为相关产物命名,明确成功判定标准,杜绝无声的半完成状态。

1. 内联对象与函数

在处理“内联对象与函数”阶段时,首先需明确相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在代码从演示环境转向共享环境时出现意外费用。 应将各种效应视为与外部世界的同步机制,而非渲染过程中派生值的替代品。

<MyComponent options={{ theme: "dark" }} />
{} !== {}
onClick={() => doSomething()}

2. 状态提升过高

在处理“2 State Lifted Too”阶段时,首先需列出相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 应将配置信息与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中于一个位置,这样操作人员无需查看整个系统结构即可进行审计。 应将各种效应视为与外部世界的同步机制,而非渲染过程中替代派生值的手段。

3. 以状态形式存储的派生状态

在处理“3个派生状态存储”阶段时,首先需明确相关契约:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 同时记录正常流程与恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的组成部分,而非后续的优化工作。 应将各种效应视为与外部世界的同步机制,而非渲染过程中派生值的替代品。 在处理“3个派生状态存储”阶段时,首先需明确相关契约:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 将此阶段视为输入与经过验证的输出之间的契约。为相关产物命名,定义成功检测标准,并杜绝无声的局部完成情况。

const [filtered, setFiltered] = useState([]);

useEffect(() => {
  setFiltered(items.filter(...));
}, [items]);

第3节:改变我一切的规则

将“第3节:规则”阶段视为可度量的对象来处理时,其效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。尽量保持渲染成本较低,只有在经过测量后才将高成本的推导操作放在记忆化之后处理。过早进行记忆化可能会掩盖过时的属性错误。

第4节:React重新渲染控制的4个层级

第4节中提到的四阶段方法在被视为可度量的界面时效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 将配置置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审计。 尽量降低渲染成本,只有在经过测量后才会将耗时的计算操作放入缓存机制中。过早使用缓存可能会掩盖过时属性带来的错误。

第一级:结构优化(最为重要)

将一级结构优化阶段视为可度量的对象来处理时,其效果最佳。在扩大范围之前,需记录一份理想运行案例、一个故障案例以及回滚说明。 同时记录正常流程与恢复流程。重试机制、人工审核环节以及错误处理都属于产品本身的功能,而非后续需要补充的内容。 尽量降低渲染成本,只有在经过评估后才能将耗时的计算操作放入缓存机制中。过早使用缓存可能会掩盖过时属性带来的错误。 将一级结构优化阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声的半完成状态。

二级:缓存机制(按需使用)

在二级缓存阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间以及令牌或查询成本。提前显示成本信息,可避免在从演示环境切换到共享环境时出现意外费用。状态应与负责数据变更的组件放在一起处理;将所有数据都存放在全局存储中会使得时间相关的问题更难被发现。

三级:引用稳定性

在三级参考稳定性阶段,应在修改代码之前明确输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审计。 状态信息应与负责执行变更的组件放在一起。将所有数据都存放在全局存储中会使得时序错误更难被发现。

const handler = () => doSomething();
const handler = useCallback(() => {
  doSomething();
}, []);
const config = useMemo(() => ({
  theme: "dark"
}), []);

四级:架构隔离

在四级架构隔离阶段,修改代码之前需明确输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复路径。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。 状态应与负责数据变更的组件放在一起管理。将所有数据都存放在全局存储中会使得时序错误更难被发现。 在四级架构隔离阶段,修改代码之前需明确输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与验证后输出之间的契约。为相关工件命名,明确成功判定标准,杜绝无声的半完成状态。

第5节:React.memo——最常被误用的优化手段

在学习React memo相关内容时,首先需明确其使用规范:所需的输入参数、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改始终符合预期。 在功能结果旁记录执行时间以及token或查询成本。提前了解这些成本,可避免从演示环境切换到共享环境时出现意外费用。 应将effect视为与外部世界的同步机制,而非渲染过程中替代派生值的手段。

何时React.memo毫无作用

在研究 React memo 的工作流程时,首先要明确相关规范:所需的输入参数、成功信号,以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 应将配置信息置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放,这样操作人员无需查看整个系统结构即可进行审计。 应将效应处理视为与外部世界的同步机制,而非渲染过程中替代派生值的手段。

React.memo 的优势所在

在处理“React 记忆化功能大显身手”这一阶段时,首先需明确相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及错误消息处理都是产品功能的一部分,而非后续的优化工作。 应将效应视为与外部世界的同步机制,而非渲染过程中替代派生值的手段。 在处理“React 记忆化功能大显身手”这一阶段时,首先需明确相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 要把这一阶段视为输入参数与经过验证的输出结果之间的契约。为相关产物命名,定义成功判定标准,绝不允许出现无声的局部完成情况。

第6节:useCallback 的陷阱

第6节:将 useCallback 视为可度量的对象使用效果最佳。在扩大应用范围之前,先记录一个成功的用例、一个失败案例以及回滚说明。在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解这些成本,就能避免在从演示环境过渡到共享环境时出现意外费用。尽量保持渲染操作的成本较低,只有在经过测量后才会将高成本的计算放在记忆化机制之后处理。过早进行记忆化可能会掩盖过时属性带来的问题。

const handleClick = useCallback(() => {
  setCount(c => c + 1);
}, []);

第7节:上下文的隐性成本

第7节“隐藏层”在被视为可度量的界面时效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 将配置置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放于一处,以便操作人员无需查看整个结构即可进行审计。 尽量降低渲染成本,只有在经过测量后才会将耗时的计算过程放入缓存中。过早使用缓存可能会掩盖过时属性带来的错误。

错误做法:

将“Bad”阶段视为可测量的界面来处理效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 同时记录正常流程和恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续的优化工作。 尽量降低渲染成本,只有在经过测量后才将耗时的计算操作放入缓存机制中。过早使用缓存可能会掩盖过时属性带来的错误。

<AuthContext.Provider value={{ user, theme }}>

更好的做法:

将“Better”阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某个步骤失败时,故障应指向单一的责任模块,而非复杂的流程链。 尽量降低渲染成本,只有在经过测量后才将耗时的计算操作放入缓存机制中。过早使用缓存可能会掩盖过时属性带来的错误。

<UserContext />
<ThemeContext />

第8节:列表、键值以及稳定性的错觉

将第8节中的列表关键步骤视为可度量的工作面,这样效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 把这一阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,绝不允许默默地只完成部分工作。 尽量降低渲染工作的成本,只有在经过测量后,才将耗时的推导过程放在缓存机制之后处理。过早使用缓存可能会掩盖过时属性带来的错误。

items.map((item, i) => <Row key={i} />)
items.map(item => <Row key={item.id} />)

第9节:个人绩效检查表

将第9节“个人阶段”视为可度量的对象来处理时,其效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,就能避免在从演示环境过渡到共享环境时出现意外账单。尽量保持渲染工作的成本较低,只有在经过测量之后才将高成本的推导操作放在记忆化机制之后处理。过早使用记忆化可能会掩盖过时的属性错误。

第10节:重大的思维转变

将第10节“大舞台”视为可测量的界面来使用效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 将配置置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放,这样操作人员无需查看整个系统结构即可进行审计。 尽量降低渲染成本,只有在经过测量后才会将耗时的计算操作放入缓存机制中。过早使用缓存可能会掩盖过时属性带来的错误。

总结:真正的秘诀

总结:将“Real”阶段视为可度量的对象时,其效果最佳。在扩大范围之前,需记录一份理想的执行日志、一个故障案例以及回滚说明。 同时记录正常流程与恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续需要补充的功能。 尽量降低渲染成本,只有在经过评估后才能将耗时的计算操作放入缓存中。过早使用缓存可能会掩盖过时属性带来的错误。 总结:将“Real”阶段视为可度量的对象时,其效果最佳。在扩大范围之前,需记录一份理想的执行日志、一个故障案例以及回滚说明。 应将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,杜绝默默完成部分任务的状况。

React性能与优化

在 React 性能优化阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新执行该步骤,而无需猜测隐藏状态。

React Hooks 与状态管理

React 模式与架构

实际 React 开发应用

React 生态系统与工具

React 学习路径(初学者→高级)

React 应用的安全性与扩展性

React 面试与职业发展

最新的 React 更新

另一个可能让你惊讶的内容

人工智能驱动的 React 开发

操作检查清单