热点技术:React性能优化总结
初学者对React可能满怀期待,觉得React可能完爆其它一切框架,甚至不切实际地认为React可能连原生的渲染都能完爆——对框架的狂热确实会出现这样的不切实际的期待。让我们来看看React的官方是怎么说的。React官方文档在Advanced
Performanec这一节,这样写道:
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:
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更新完之后,做一些收尾的工作)
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不一致,需要更新。
C6节点,绿色SCU (true),表示需要更新,然后vDOMEq红色,表示虚拟DOM不一致,更新DOM。
C8节点,绿色SCU (true),表示需要更新,然后vDOMEq绿色,表示虚拟DOM一致,不更新DOM。
{…this.props} (不要滥用,请只传递component需要的props,传得太多,或者层次传得太深,都会加重shouldComponentUpdate里面的数据比较负担,因此,也请慎用spread attributes( Component {…props} / ))。
map里面添加key,并且key不要使用index(可变的)。具体可参考使用Perf工具研究React Key对渲染的影响
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)
var PureRenderMixin = require(react-addons-pure-render-mixin); React.createClass({ mixins: [PureRenderMixin], render: function() { return div className={this.props.className} foo /div
Shallow Compare (es6)
var shallowCompare = require(react-addons-shallow-compare); export class SampleComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this, nextProps, nextState); render() { return div className={this.props.className} foo /div
es7装饰器的写法:
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 )
pureRender很简单,就是把传进来的component的shouldComponentUpdate给重写掉了,原来的shouldComponentUpdate,无论怎样都是return ture,现在不了,我要用shallowCompare比一比,shallowCompare代码及其简单,如下:
function shallowCompare(instance, nextProps, nextState) { return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState);
缺点
shallowEqual其实只比较props的第一层子属性是不是相同,就像上述代码,props 是如下
detail: { name: "123", age: "123"
他只会比较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 是这样的:
import { is } from immutable; shouldComponentUpdate: (nextProps = {}, nextState = {}) = { const thisProps = this.props || {}, thisState = this.state || {}; if (Object.keys(thisProps).length !== Object.keys(nextProps).length || Object.keys(thisState).length !== Object.keys(nextState).length) { return true; for (const key in nextProps) { if (!is(thisProps[key], nextProps[key])) { return true; } for (const key in nextState) { if (thisState[key] !== nextState[key] || !is(thisState[key], nextState[key])) { return true; } return false; react-immutable-render-mixin
这是一个facebook/immutable-js的react pure render mixin 的库,可以简化很多写法。
使用react-immutable-render-mixin可以实现装饰器的写法。
import React from react; import { immutableRenderDecorator } from react-immutable-render-mixin; @immutableRenderDecorator class Test extends React.Component { render() { return div /div
无状态组件
为了避免一定程度的浪费,react官方还在0.14版本中加入了无状态组件,如下:
// es6 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
react组件进阶 之代码性能工具的使用 在严格模式下,虽然不能监控到具体的副作用代码,但它会将不能具有副作用的函数调用两遍,以便发现问题。(这种情况,仅在开发模式下有效)
React 函数式组件性能优化指南 本文只介绍函数式组件特有的性能优化方式,类组件和函数式组件都有的不介绍,比如 key 的使用。另外本文不详细的介绍 API 的使用,后面也许会写,其实想用好 hooks 还是蛮难的。
react组件优化,如何避免没有必要的render导致性能浪费 在开发过程中,肯定会出现层层嵌套的关系组件,当我们使用Component时,父组件中的state或者props发生更新时,无论子组件中的state和props是否更新,都会触发子组件的更新,会导致很多没有必要的render,浪费很多性能。今天这这篇文章就教大家如何来解决这个问题
React Native 迎来重大架构升级,性能将大幅提升 7 月 14 日,React Native 核心团队的 Joshua Gross 在 Twitter 说,RN 的新架构已经在 Facebook 内部落地了,并且 99%的代码已经开源。这次的架构升级“蓄谋已久”,Joshua 说他们从 2018 年 1 月就开始规划了。
react性能优化 刚开始写react可能只是写出来完成业务就完了,后期审查代码发现可能很多地方其实都可以优化,之前可能有些地方似是而非,在此小结一下。 Virtual DOM react引入了一个叫做虚拟DOM的概念,安插在JavaScript逻辑和实际的DOM之间。
相关文章
- 性能测试人员应该具备的技术知识
- 软件测试技术知识之性能测试流程总结
- 实时光线追踪技术:业界发展近况与未来挑战
- CASE函数 sql server——分组查询(方法和思想) ref和out 一般处理程序结合反射技术统一执行客户端请求 遍历查询结果集,update数据 HBuilder设置APP状态栏
- 猿创征文|HCIE-Security Day55:反病毒技术
- K8S原理剖析:云原生技术的前世今生
- 微服务技术系列教程(19) - SpringCloud- 服务治理Eureka(详解)
- 技术分享 | app自动化测试(Android)-- 属性获取与断言
- 移动网站性能优化:网页加载技术概览
- MongoDB最佳实践及性能优化(DTCC中国数据库技术大会分享PPT)
- NoSQL Databases技术资料整理汇总
- 《Spark大数据分析:核心概念、技术及实践》一2.3 一个单独的Scala应用程序
- 数据库数据处理性能提升技术
- 技术更新!10个MySQL性能调优技巧
- 《循序渐进Linux(第2版) 基础知识 服务器搭建 系统管理 性能调优 虚拟化与集群应用》——1.4 用虚拟机技术学习Linux
- 软件项目技术点(11)——大图变小图提高绘图性能
- 《Web应用漏洞侦测与防御:揭秘鲜为人知的攻击手段和防御技术》——导读
- handyJson的技术内核
- 《Python自动化运维:技术与最佳实践》一1.1 系统性能信息模块psutil
- 《数据库技术原理与应用教程(第2版)》——2.3 数据库系统的特点
- 《Spark大数据处理:技术、应用与性能优化》——2.2 Spark集群初试
- Java序列化技术性能分析(JDK原生与Protostuff)
- 掌握设计开发维护大型网站的技术之性能
- 集成了云安全技术 —— 准能公司下一代防火墙获美国NSS实验室的入侵防护性能评测“推荐级”认证
- 秋色园QBlog技术原理解析:性能优化篇:读写分离与文本数据库(十八)
- 秋色园QBlog技术原理解析:性能优化篇:用户和文章计数器方案(十七)
- 秋色园QBlog技术原理解析:性能优化篇:access的并发极限及超级分库分散并发方案(十六)
- 秋色园QBlog技术原理解析:性能优化篇:字节、缓存、并发(十二)