写点什么

什么时候要在 React 组件中写 shouldComponentUpdate?

  • 2016-06-30
  • 本文字数:3099 字

    阅读完需:约 10 分钟

生命中一半的时间都用来写 JavaScript 的 James K Nelson 最近发表了一篇文章,标题是《 Should I use shouldComponentUpdate? 》。他在这篇文章中介绍了应该在什么情况下使用 React 组件中的shouldComponentUpdate方法。

接触过 React 的人应该都知道它是一个非常快的前端框架,或许也听说过shouldComponentUpdate可以让它更快。但你知不知道它们在什么情况下才能发挥作用?也就是说,你知道什么时候需要动手写shouldComponentUpdate方法吗?

James 指出,如果你在 React 组件中写了shouldComponentUpdate方法后不能获得可测量的,并且是可察觉到的性能提升,那就不要写。

你的意思是我不应该用它?

按照 React 团队的说法,shouldComponentUpdate是保证性能的紧急出口,既然是紧急出口,那就意味着我们轻易用不到它。但既然有这样一个紧急出口,那说明有时候它还是很有必要的。所以我们要搞清楚到底什么时候才需要使用这个紧急出口。

为了讲清楚这个问题,James 对 React 的渲染机制做了深入地剖析。

他首先指出:

添加shouldComponentUpdate方法一般都会拖慢组件的更新速度。

为什么会这样呢?因为在他看来,React 基本上就是一个非常聪明的shouldComponentUpdate实现。它不仅知道应该在什么时候更新组件,还知道应该如何更新组件,并且这两件事情它都做得很好。那么 React 是如何知道是否应该更新组件的呢?这要从组件中的render方法说起。

尽管在写代码时,我们看到render返回的都是 JSX 或者ReactElement,但实际上,它返回的都是下面这种普通的 JavaScript 对象:

复制代码
{
type: 'ul',
props: { className: 'what-do-you-want-to-do-tonight' },
children: [{
type: 'li',
children: 'The same thing we do every night, pinky.'
},]
}

React 就是用这种对象来描述要在界面中渲染的标签。如果跟上次渲染时所用的对象比较,数据没有发生变化,显然就不用更新界面中的 DOM。

换句话说,React 已经替我们实现了一个shouldComponentUpdate。为了简化,我们可以假装props不是绑在组件的this上的,而是直接传给了render,那么 React 的实现基本上就是下面这样的:

复制代码
shouldComponentUpdate(nextProps) {
return !deepEquals(render(this.props), render(nextProps))
}

你是知道的,对于比较小的对象来说,deepEquals很快,但如果是个层层嵌套的大家伙,它的速度就不行了。因此我们可以得出第一条结论:

如果render的返回值很小,但props是个大家伙,那自己写shouldComponentUpdate很可能不会带来什么好结果。

那这是不是说,如果render返回的值足够大,我们自己写shouldComponentUpdate就会比较划算呢?实际上也不尽然。

讲到这里,James 又给出了他观察到的第二个事实:

使用shouldComponentUpdate得到的收益一般是微乎其微的。

他举了一个例子:比如要渲染一个table,我们从props中得到数据,然后又对这些数据做了些计算。并且这些数据都是放在 Immutable.js 的结构中的,因此通过比较引用是否相等就能判断出props是否发生了变化。

在这样一个场景中,如果我们自己写shouldComponentUpdate,那速度要比 React 默认实现的处理速度快很多。James 说他观察到的结果是最少快十倍!对,你没看错,他确实是这样说的,但他紧接着又说:

不足一毫秒的渲染时间在速度提升了 10 倍之后,依然也还是不足一毫秒。

嗯,我也觉得他这是在耍我们。

James 还搬出大神高德纳的那句名言来警告我们不要掉进过早优化的陷阱。为了引起足够的重视,他又指出了使用 shouldComponentUpdate所引发的问题:

shouldComponentUpdate很难维护

React 团队说shouldComponentUpdate是个紧急出口,而不是加速按钮应该就是出于这个原因。但 James 给出了一个更形象的比喻,他说用shouldComponentUpdate就是没有采取安全措施的性行为。

因为他觉得有时候很有必要写shouldComponentUpdate,并且那些时候shouldComponentUpdate肯定会让你的 app 有更好的表现。但是这也是导致 bug 的主要原因之一,并且还都是一些不太容易察觉的 bug。接着他又给出了几个具体的例子,并指出这些 bug 在测试中很难发现。如果在给客户演示的时候跳出来,那后果就不堪设想了。

什么时候需要写 shouldComponentUpdate?

因此还是回到了最初的那个问题上,什么时候需要自己动手写shouldComponentUpdate方法?他再次重申了前文中给出的那个答案:

只有经过测量,发现有了shouldComponentUpdate后组件的渲染速度确实有可察觉的提升,你才应该用它。

James 还在 fiddle 中给了一个例子,供我们练习如何测量,并比较使用 shouldComponentUpdate前后的渲染速度。

在开始测量之前先搞清楚如何在你的浏览器中打开分析器(如果你还不知道怎么做,Chrome 请看这里,Firefox 看这里)。

测量的过程很简单:

第一步:测量标准版

先从点击一个动作时render所花的时间开始测量:

  1. 在 fiddle 例子的窗口中打开 JavaScript 分析器。
  2. 开始记录。
  3. 点击"Toggle synergy!",让页面循环 5 秒钟左右。
  4. 点击开始记录时点过的那个按钮,停止记录。
  5. 按“self”时间排序,找到耗时最多的那个render方法,应该在列表的顶端附近。把它用的时长记下来,这里我们要的是“总时长”,即render本身所用的时间及它调用的函数所用的时间。

如果在这个列表顶部没找到render,那么恭喜你!你完全没必要写shouldComponentUpdate,最起码在解决掉其他性能问题之前没必要。

第二步:测量有shouldComponentUpdate的版本

这一步是要测量给App组件添加了shouldComponentUpdate之后渲染所花的时间。下面有个提前准备好的实现,你可以把它加到组件中:

复制代码
shouldComponentUpdate(nextProps, nextState) {
return !Immutable.is(this.state.synergy, nextState.synergy)
},

加好之后,按步骤一中的过程测量一遍。得到测量结果后,还要找到shouldComponentUpdate用的时间,然后加上去。应该差不多像下面这样:

组件中有shouldComponentUpdaterender

shouldComponentUpdate

就是这样,测过之后你就能 _ 大概 _ 知道shouldComponentUpdate能带来什么样的好处了。注意这里说的是 _ 大概 _,因为这种方法得不到精确的结果。一定要记住,真实结果跟测量结果比可能会有很大的差异,不信你可以多试几次。

做出决定

既然测量结果不精确,那我们凭什么做出决定呢?要凭好得不容置疑的测量结果。

那什么才算是好得不容置疑的结果呢?按照 James 的经验,如果加上shouldComponentUpdate之后渲染时间减少了一半,那用shouldComponentUpdate应该是真的对你有好处的。但同时也不要忘记,只有原来的渲染时间足够长时,这种性能上的提升才是有意义的。假如本来只用了 100ms,那你折腾半天加快的那点速度人们依然是感觉不到的。

所以在最终要做决定的时候,你要记住使用shouldComponentUpdate会带来维护上的挑战;并且测量结果是不准确的;而且性能改善的幅度还要是能感觉到的那种,只有记住这三点,你才能做出正确的决定。当然,这一切的前提是你的shouldComponentUpdate实现是没有问题的。

写出有效的 shouldComponentUpdate

在经过不懈地努力找到应该使用shouldComponentUpdate的点后,接下来的问题就是应该怎么写呢?

James 给出的答案非常:“Immutable.js!”。看到这个答案你的感觉是不是像看到下面这幅画一样?

当然,Immutable.js 不是唯一的答案,用Object.assign也可以,关键是不可变的状态。

如果你觉得 Immutable.js 也解决不了你的问题,James 又提出了一个更高级的解决方案:结构良好的状态。不过如果你想知道怎么才能做出结构良好的状态,只能听他下回分解了。


感谢韩婷对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-06-30 18:0023852
用户头像

发布了 45 篇内容, 共 26.2 次阅读, 收获喜欢 11 次。

关注

评论

发布
暂无评论
发现更多内容

开源赋能 普惠未来|腾讯寄语2023开放原子全球开源峰会

开放原子开源基金会

开源 开放原子全球开源峰会 开放原子

硬核!力扣官方首发了这套1568页LeetCode算法刷题笔记(彩页版)

Java你猿哥

面试 算法 LeetCode ssm

爱了,Spring Cloud Alibaba内部微服务架构笔记真的太牛了

Java你猿哥

Java 微服务 微服务架构 Spring Cloud ssm

行业DBA走进华为,共建数据库生态

YG科技

5年Java经验字节社招:15天3次面试,成功拿下Offer

程序知音

Java java面试 大厂面试 后端技术 Java面试八股文

软件测试/测试开发丨接口自动化测试学习笔记分享

测试人

程序员 软件测试 自动化测试 接口测试 测试开发

Csdn上20W+阅读,这份“Java核心万字宝典”霸榜9天,太香了

Java

🔥笔下生花,与时舒卷,InfoQ写作社区6月优质更文活动来啦!

InfoQ写作社区官方

热门活动 6 月 优质更文活动

阿里、字节大佬共创的Netty核心原理手册,必须是全网No.1

程序知音

Java Netty java架构 Java进阶 后端技术

大语言模型的创意能力到底几何?探索从GPT-2到GPT-4的演进

Baihai IDP

人工智能 自然语言处理 大模型 白海科技 企业号 6 月 PK 榜

Java上进了,JDK21 要来了,并发编程再也不是噩梦了

Java你猿哥

Java jdk ssm

技术驱动,数据赋能,华为云GaussDB给世界一个更优选择

YG科技

【2023华为云CodeArts Build 实战训练营】云端实战-玩转编译构建

华为云PaaS服务小智

云计算 华为云 华为开发者大会2023

你还在用 BeanUtils?试试 MapStruct,优雅的对象转换解决方案!

Java你猿哥

Java ssm map

App Cleaner & Uninstaller:mac专业的系统清理优化工具

Rose

App Cleaner 系统清理工具 苹果mac系统优化 App Cleaner 破解

写给程序员的可逆计算理论辨析补遗

canonical

低代码 可逆计算 范畴论

深度解析Seata AT 模式中性能优化与隔离保障的平衡之道

Java你猿哥

Java 数据 ssm 脏读

宝兰德应用服务器软件与华为云GaussDB完成兼容互认证

YG科技

开源赋能 普惠未来|OpenHarmony诚邀您参与2023开放原子全球开源峰会

开放原子开源基金会

开源 OpenHarmony 开放原子

如何在企业中培养平台工程文化?

SEAL安全

平台工程 平台工程文化

强渡大渡河!华为云GaussDB支撑华为MetaERP系统全面替换

YG科技

从BeyondCampus最佳实践,洞察高校零信任发展趋势

权说安全

开源赋能 普惠未来|元遨/CARSMOS诚邀您参与2023开放原子全球开源峰会

开放原子开源基金会

开源 CARSMOS 元遨

自动化回归测试平台 AREX 前端架构演变史 —— Tabs 动态组件设计

AREX 中文社区

Vue 前端 软件测试

面渣逆袭必看!Java中高级面试指南,Github标星4.3k+!

Java java面试 Java八股文 Java面试题 Java面试八股文

国产自研数据库是更新换代首选

YG科技

如何从零实现一个简单的Spring Bean容器

Java你猿哥

Java spring ssm Spring Bean Java web

简单好用的便利贴工具:Sticky 激活版

真大的脸盆

Mac Mac 软件 便利贴工具 便利贴软件

火山引擎DataLeap的Catalog系统搜索实践(三):Learning to rank与后续工作

字节跳动数据平台

数据湖 数据化 数据平台 大数据分析 DataLeap

华为云GaussDB以技术创新引领金融行业分布式转型

YG科技

[EuroSys2023 Best Poster] 面向动态图的极低时延GNN推理采样服务

阿里云大数据AI技术

人工智能 机器学习 推理 企业号 6 月 PK 榜 DGS

什么时候要在React组件中写shouldComponentUpdate?_语言 & 开发_吴海星_InfoQ精选文章