热点技术:React性能优化总结

初学者对React可能满怀期待,觉得React可能完爆其它一切框架,甚至不切实际地认为React可能连原生的渲染都能完爆——对框架的狂热确实会出现这样的不切实际的期待。让我们来看看React的官方是怎么说的。React官方文档在Advanced
Performanec这一节,这样写道:


  1. One of the first questions people ask when considering React for a project is whether their application will be as fast and responsive as an equivalent non-React version 

显然React自己也其实只是想尽量达到跟非React版本相若的性能,

你所不知道的render

react的组件渲染分为初始化渲染和更新渲染。在初始化渲染的时候会调用根组件下的所有组件的render方法进行渲染,如下图(绿色表示已渲染,这一层是没有问题的):

但是当我们要更新某个子组件的时候,如下图的绿色组件(从根组件传递下来应用在绿色组件上的数据发生改变):

我们的理想状态是只调用关键路径上组件的render,如下图:

但是react的默认做法是调用所有组件的render,再对生成的虚拟DOM进行对比,如不变则不进行更新。这样的render和虚拟DOM的对比明显是在浪费,如下图(黄色表示浪费的render和虚拟DOM对比)

Tips:

  • 拆分组件是有利于复用和组件优化的。
  • 生成虚拟DOM并进行比对发生在render()后,而不是render()前。

更新阶段的生命周期

  • componentWillReceiveProps(object nextProps):当挂载的组件接收到新的props时被调用。此方法应该被用于比较this.props

    nextProps以用于使用this.setState()执行状态转换。(组件内部数据有变化,使用state,但是在更新阶段又要在props改变的时候改变state,则在这个生命周期里面)
  • shouldComponentUpdate(object nextProps, object nextState): -boolean 当组件决定任何改变是否要更新到DOM时被调用。作为一个优化实现比较this.props 和 nextProps 、this.state 和 nextState ,如果React应该跳过更新,返回false。
  • componentWillUpdate(object nextProps, object nextState):在更新发生前被立即调用。你不能在此调用this.setState()
  • componentDidUpdate(object prevProps, object prevState): 在更新发生后被立即调用。(可以在DOM更新完之后,做一些收尾的工作)

Tips:

  • React的优化是基于shouldComponentUpdate的,该生命周期默认返回true,所以一旦prop或state有任何变化,都会引起重新render。

shouldComponentUpdate

react在每个组件生命周期更新的时候都会调用一个shouldComponentUpdate(nextProps,
nextState)函数。它的职责就是返回true或false,true表示需要更新,false表示不需要,默认返回为true,即便你没有显示地定义
shouldComponentUpdate 函数。这就不难解释上面发生的资源浪费了。

为了进一步说明问题,我们再引用一张官网的图来解释,如下图(
SCU表示shouldComponentUpdate,绿色表示返回true(需要更新),红色表示返回false(不需要更新);vDOMEq表示虚拟DOM比对,绿色表示一致(不需要更新),红色表示发生改变(需要更新)):

根据渲染流程,首先会判断shouldComponentUpdate(SCU)是否需要更新。如果需要更新,则调用组件的render生成新的虚拟DOM,然后再与旧的虚拟DOM对比(vDOMEq),如果对比一致就不更新,如果对比不同,则根据最小粒度改变去更新DOM;如果SCU不需要更新,则直接保持不变,同时其子元素也保持不变。

  • C1根节点,绿色SCU (true),表示需要更新,然后vDOMEq红色,表示虚拟DOM不一致,需要更新。
  • C2节点,红色SCU (false),表示不需要更新,所以C4,C5均不再进行检查
  • C3节点同C1,需要更新
  • C6节点,绿色SCU (true),表示需要更新,然后vDOMEq红色,表示虚拟DOM不一致,更新DOM。
  • C7节点同C2
  • C8节点,绿色SCU (true),表示需要更新,然后vDOMEq绿色,表示虚拟DOM一致,不更新DOM。

带坑的写法:

  • {…this.props}
    (不要滥用,请只传递component需要的props,传得太多,或者层次传得太深,都会加重shouldComponentUpdate里面的数据比较负担,因此,也请慎用spread
    attributes(<Component {…props} />))。
  • ::this.handleChange()。(请将方法的bind一律置于constructor)
  • this.handleChange.bind(this,id)
  • 复杂的页面不要在一个组件里面写完。
  • 请尽量使用const element。
  • map里面添加key,并且key不要使用index(可变的)。具体可参考使用Perf工具研究React Key对渲染的影响
  • 尽量少用setTimeOut或不可控的refs、DOM操作。
  • 数据尽可能简单明了,扁平化。

性能检测工具

React官方提供的:React.addons.Perf

react官方提供一个插件React.addons.Perf可以帮助我们分析组件的性能,以确定是否需要优化。
打开console面板,先输入Perf.start()执行一些组件操作,引起数据变动,组件更新,然后输入Perf.stop()。(建议一次只执行一个操作,好进行分析)
再输入Perf.printInclusive查看所有涉及到的组件render,如下图(官方图片):

或者输入Perf.printWasted()查看下不需要的的浪费组件render,如下图(官方图片):

优化前:

优化后:

其他的检测工具

react-perf-tool为React应用提供了一种可视化的性能检测方案,该工程同样是基于React.addons,但是使用图表来显示结果,更加方便。

React官方的解决方案

PureRenderMixin(es5)


  1. var PureRenderMixin = require('react-addons-pure-render-mixin'); 
  2. React.createClass({ 
  3.   mixins: [PureRenderMixin], 
  4.  
  5.   render: function() { 
  6.     return <div className={this.props.className}>foo</div>; 
  7.   } 
  8. }); 

Shallow Compare (es6)


  1. var shallowCompare = require('react-addons-shallow-compare'); 
  2. export class SampleComponent extends React.Component { 
  3.   shouldComponentUpdate(nextProps, nextState) { 
  4.     return shallowCompare(this, nextProps, nextState); 
  5.   } 
  6.  
  7.   render() { 
  8.     return <div className={this.props.className}>foo</div>; 
  9.   } 

es7装饰器的写法:


  1. import pureRender from "pure-render-decorator" ... @pureRender class Person extends Component { render() { console.log("我re-render了"); const {name,age} = this.props; return ( <div> <span>姓名:</span> <span>{name}</span> <span> age:</span> <span>{age}</span> </div> 
  2.       ) 
  3.   } 

pureRender很简单,就是把传进来的component的shouldComponentUpdate给重写掉了,原来的shouldComponentUpdate,无论怎样都是return
ture,现在不了,我要用shallowCompare比一比,shallowCompare代码及其简单,如下:


  1. function shallowCompare(instance, nextProps, nextState) { 
  2.   return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState); 

缺点

shallowEqual其实只比较props的第一层子属性是不是相同,就像上述代码,props 是如下


  1.   detail: { 
  2.     name: "123", age: "123" 
  3.   } 

他只会比较props.detail ===nextProps.detail,导致在传入复杂的数据的情况下,优化失效。

immutable.js

我们也可以在 shouldComponentUpdate() 中使用使用 deepCopy 和 deepCompare 来避免无必要的 render(),但 deepCopy 和 deepCompare 一般都是非常耗性能的。

Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。

Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。请看下面动画:

Immutable 则提供了简洁高效的判断数据是否变化的方法,只需 === 和 is 比较就能知道是否需要执行 render(),而这个操作几乎 0 成本,所以可以极大提高性能。修改后的 shouldComponentUpdate 是这样的:


  1. import { is } from 'immutable'; 
  2.  
  3. shouldComponentUpdate: (nextProps = {}, nextState = {}) => { 
  4.   const thisProps = this.props || {}, thisState = this.state || {}; 
  5.  
  6.   if (Object.keys(thisProps).length !== Object.keys(nextProps).length || 
  7.       Object.keys(thisState).length !== Object.keys(nextState).length) { 
  8.     return true; 
  9.   } 
  10.  
  11.   for (const key in nextProps) { 
  12.     if (!is(thisProps[key], nextProps[key])) { 
  13.       return true; 
  14.     } 
  15.   } 
  16.  
  17.   for (const key in nextState) { 
  18.     if (thisState[key] !== nextState[key] || !is(thisState[key], nextState[key])) { 
  19.       return true; 
  20.     } 
  21.   } 
  22.   return false; 
  23.  
  24. react-immutable-render-mixin 

这是一个facebook/immutable-js的react pure render mixin 的库,可以简化很多写法。
使用react-immutable-render-mixin可以实现装饰器的写法。


  1. import React from 'react'; 
  2. import { immutableRenderDecorator } from 'react-immutable-render-mixin'; 
  3.  
  4. @immutableRenderDecorator 
  5. class Test extends React.Component { 
  6.   render() { 
  7.     return <div></div>; 
  8.   } 

Immutable 详解及 React 中实践

无状态组件

为了避免一定程度的浪费,react官方还在0.14版本中加入了无状态组件,如下:


  1. // es6 
  2. const HelloMessage = (props) => <div>Hello {props.name}</div>; 

高阶组件(接下来的方向)

大部分使用mixin和class extends的地方,高阶组件都是更好的方案——毕竟组合优于继承

参考文章

使用ES6编写React应用(4):使用高阶组件替代Mixins
Mixin 已死,Composition 万岁

React同构直出(接下来方向)

同构基于服务端渲染,却不止是服务端渲染。

React在减少重复渲染方面确实是有一套独特的处理办法,那就是virtual DOM,但显示在首次渲染的时候React绝无可能超越原生的速度。因此,我们在做优化的时候,接下来可以做的事情就是:

首屏时间可能会比较原生的慢一些,但可以尝试用React Server Render (又称Isomorphic)去提高效率

作者:Pines_Cheng

来源:51CTO

时间: 2025-01-02 20:06:58

热点技术:React性能优化总结的相关文章

一起谈.NET技术,性能优化总结

最近在领导的要求下做了一下项目的优化,总结如下: 1. 使用存储过程(如果在程序里用exec 存储过程参数,这样执行似乎并没有快多少). 在数据库里是预编译的,也不需要在字符串传输上花费大量时间. 防sql注入攻击. 2. 尽量优化数据库语句,使逻辑尽量简单       a) 还有就是在使用函数时,charindex >like > padindex  效率依次递减. b) 查询字段是否包含在以,分隔的字段串时,最好不要用in  速度非常慢. 还有好多,可以总结的,这里就不再描述了. 3. 

React Native JS Module 加载性能优化

关于React Native 性能 React Native 在手淘中已开始逐步推广, 在拍立淘首页的使用场景中,我们发现React Native并没有想 象中的那么快,实测效果在离线状态下性能甚至比不过H5 WindVane,React Native的UI会出现延迟渲 染存在视觉差,经过具体的代码性能测试,整个过程平均在300 ms (IPhone 5S机型下,整个JS文件 400K), 然后其核心系统调用代码加载解析整个JS (JSEvaluateScript)耗时在220 ms左右,在目前

MongoDB最佳实践及性能优化(DTCC中国数据库技术大会分享PPT)

云数据库 MongoDB 版 基于飞天分布式系统和高性能存储,提供三节点副本集的高可用架构,容灾切换,故障迁移完全透明化.并提供专业的数据库在线扩容.备份回滚.性能优化等解决方案. 了解更多 上周五在北京DTCC分享了「32 Tips to Boost MongoDB Performance」,本文是分享的PPT以及重要内容的注解. 注解:本次分享主要「自底向上」的介绍提升 MongoDB 服务性能需要注意的问题,从硬件.操作系统.服务端一直到应用端,前面3个层次的建议主要面向DBA及运维人员,

APMCon2017 | 一大波技术大神来袭,你要的性能优化干货这里全都有

APMCon2017 | 一大波技术大神来袭,你要的性能优化干货这里全都有 在波涛汹涌的技术世界里,如果不希望自己的小船说翻就翻,就需要跟着大神多学学, 所谓听君一席话,胜读十年书.现在就有个跟着大神学技术的好机会 就在今年的8月10-11日,听云联合了极客邦科技 / InfoQ将共同主办国内第二届应用性能管理大会-APMCon 2017,会议内容聚焦了行业内最新的技术和最接地气的实践案例,将共同探讨APM相关的性能优化.技术方案以及创新思路,可以为更多的行业从业者指点应用效能提升的迷津.就在第

【阿里在线技术峰会】何登成:AliSQL性能优化与功能突破的演进之路

本文根据阿里高级数据库专家何登成在首届阿里巴巴在线技术峰会上的分享整理而成.他主要介绍了AliSQL相对于MySQL进行的性能优化.通过大连接.高并发下的数据库稳定性保障和库存热点更新两个问题的解决方案介绍了高低水位限流和线程池的使用方法以及库存热点优化的三步改进,最后提出了AliSQL的完整生态体系. 直播视频:点此进入 PDF下载:点此进入 以下为整理内容. 背景:当使用官方的MySQL时,发现在我们的体量下会面临比较大的挑战,所以,2011年我们在MySQL的基础上做了一些功能增强和bug

Oracle数据库性能优化技术

摘要: Oracle数据库是当前应用最广泛的大型数据库之一,而其性优化直接关系到系统的运行效率.本文以数据库性能优化的基本原则为出发点,阐述了在数据库设计阶段如何避免竞争和如何优化数据访问,在数据库运行阶段如何从操作系统和数据库实例级别上调整内存和I/O来达到数据库性能优化的各种技术. 关键词:Oracle数据库:性能优化:内存:I/O 1. 引言 随着网络应用和电子商务的不断发展,各个站点的访问量越来越大,数据库规模也随之不断的扩大,数据库系统的性能问题就越来越突出,因此,如何对数据库进行调优

网站性能优化之CSS无图片技术 提高价值速度

一.无图片技术定义 在不使用CSS Image(通过CSS的引入的背景图片,不包括img标签内的图片)情况下生成类似图片效果的技术;换句话的意思就是在使用纯CSS生成类似图片效果的技术. 二.为什么要"无图片"? 首先我们通过yslow的statistics查看新浪微博最新版首页的文件,得到Stylesheet File(CSS文件)大小为206.8K, CSS Image大小为623.8K.明显发现CSS文件比CSS Image小很多. 当然单纯拿这两个来比,还不能说明什么. 下面我

网站性能优化之CSS无图片技术

一.无图片技术定义 在不使用CSS Image(通过CSS的引入的背景图片,不包括img标签内的图片)情况下生成类似图片效果的技术;换句话的意思就是在使用纯CSS生成类似图片效果的技术. 二.为什么要"无图片"? 首先我们通过yslow 的statistics查看新浪微博最新版首页的文件,得到Stylesheet File(CSS文件)大小为206.8K, CSS Image大小为623.8K.明显发现CSS文件比CSS Image小很多. 当然单纯拿这两个来比,还不能说明什么. 下面

Web业务性能优化技术总结

前言 Web业务的性能优化是一个系统工程,既有深度,又有广度.以下所简称性能均特指Web业务性能. 技术的广度上,主要从大背景下考虑到其各个相关方,基于共同的数据指标发掘和评估方案. 技术的深度上是一个渐进和迭代的过程.可以从性能的本质展到目前各端的主要优化方向. 性能的本质 性能的本质是快速传播, 要素是内容(数据)和流程,效果是:完备.快速.完备不是完整,而是接受的信息要一致,没有歧义.流程是内容处理的过程和方法. 流程从广义上看来源于后台服务器,以网络和客户端为媒介,以页面形式到达用户.落