react性能优化
2017-08-28 15:45
381 查看
写在前面的话:
要想解决问题,首先得找到问题的根源,所以,说起性能分析,还是要从其生命周期和渲染机制说起。
但是当我们要更新某个子组件的时候,是从根组件传递下来应用在子组件上的数据发生改变。
我们只是希望调用关键路径上组件的render就好了。
但是,
react的默认做法是调用所有组件render,再对生成的虚拟DOM进行对比,如不变就不进行更新。这样的render和虚拟DOM的对比明显实在浪费时间。
注意:
拆分组件是有利于复用和组件优化的,其次,生成虚拟DOM并进行对比发生在render之后,而不是render之前。
总得来说,父亲组件的props和state发生变化时,他和他的子子孙孙组件等后代都要重新渲染。
1.componementWillReceiveProp:当挂载的组件接受到新的props时会被调用,此方法应该被用于比较this.props和nextProps以用于使用this.setState()执行状态转换,(组件内部数据有变化,使用state,但是在更新阶段又要在props改变的时候改变state,则在这个生命周期里面)
2.shouldComponentUpdate:当组件决定任何改变是否要更新到DOM时被调用,作为一个优化实现比较this.props和nextProps,this.state和nextState,如果应该跳过更新,返回false。
3.componentWillUpdate:在更新发生前被立即调用,此时不能调用this.setState()
4.componentDidUpdate:在更新发生后被立即调用。(可以在DOM更新完之后,做一些收尾工作)
react的优化是基于shouldComponentUpdate的,该生命周期默认返回true,所以一旦prop或state有任何变化,都会引起重新render。
所以,可以得出react的性能优化就是围绕shouldComponentUpdate(SCU)方法来进行的,其优化无外乎两点:
缩短SCU方法的执行时间(或者直接不执行)
没必要的渲染,SCU就该返回false
shouldComponentUpdate:
react在每个组件生命周期更新的时候都会调用一个
根据渲染流程,首先会判断shouldcomponentUpdate是否需要更新,如果需要更新,则调用组件的render生成新的虚拟DOM,然后再与旧的虚拟DOM进行对比,如果对比一致就不更新,如果对比不同,则根据最小粒度改变去更新DOM,如果scu不需要更新,则直接保持不变,同时其子元素也保持不变。
注意:错误写法
{…this.props}(不能滥用,只传递component需要的props即可,传递的太多,或者层次传的太深,都会加重shuoldComponentUpdate,其里面的数据比较负担,因此一定要慎重使用
复杂的页面不要在一个组件中写完
尽量使用
map里面添加key,并且key不要使用index(可变的)
尽量少的使用setTimeout或者不可控的refs,DOM操作
props和state的数据尽可能简单明了,扁平化。
使用return null而不是css的
react官方提供一个插件react.addons.perf可以帮助我们分析组件的性能,以确认是否需要优化。
Perf 是react官方提供的性能分析工具。Perf最核心的方法莫过于
操作:
打开console面板,先输入Perf.start()执行一些组件操作,引起数据变动,组件更新,然后输入Perf.stop()。(建议一次只执行一个操作,好进行分析)
然后在输入Perf.printInclusive将所有涉及到的组件render打印下来。(官方图片)
或者输入Perf.printWasted()查看不需要的浪费组件的render。
优化前:
优化后:
1.PureRenderMixin(基于es5)
2.Shallow Compare(基于es6)
pureRender很简单,就是把穿进来的component的shouldComponentUpdate给重写掉,原来的shouldComponentUpdate,无论怎样都是
但是这样做还是有缺点的:
shallowEqual其实只比较props的第一层,子属性是不是相同,如果props是如下:
他只会比较
Immutable Date就是一旦创建,就不能再被更改的数据。对Immutable对象的任何修改或者添加删除操作都会返回一个新的Immutable对象。
Immutable实现的原理是Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变,同时为了避免deeepCopy把所有的节点都复制一遍带来的性能损耗,Immutable使用了,Structural Sharing(结构共享)即如果对象树中一个节点发生变化,只修改这个节点和受他影响的父节点,其他节点则进行共享。
Immutable则提供了简洁高效的判断数据是否变化的方法,只需要===和is比较就能知道是否需要执行render(),而这个操作几乎0成本,所以可以极大提高性能,修改后的shouldComponentUpdate是这样的:
要想解决问题,首先得找到问题的根源,所以,说起性能分析,还是要从其生命周期和渲染机制说起。
1.渲染机制
react的组件渲染分为初始化渲染和更新渲染,在初始化渲染的时候会调用根组件下的所有组件的render方法进行渲染。但是当我们要更新某个子组件的时候,是从根组件传递下来应用在子组件上的数据发生改变。
我们只是希望调用关键路径上组件的render就好了。
但是,
react的默认做法是调用所有组件render,再对生成的虚拟DOM进行对比,如不变就不进行更新。这样的render和虚拟DOM的对比明显实在浪费时间。
注意:
拆分组件是有利于复用和组件优化的,其次,生成虚拟DOM并进行对比发生在render之后,而不是render之前。
总得来说,父亲组件的props和state发生变化时,他和他的子子孙孙组件等后代都要重新渲染。
2.更新阶段的生命周期
1.componementWillReceiveProp:当挂载的组件接受到新的props时会被调用,此方法应该被用于比较this.props和nextProps以用于使用this.setState()执行状态转换,(组件内部数据有变化,使用state,但是在更新阶段又要在props改变的时候改变state,则在这个生命周期里面)
2.shouldComponentUpdate:当组件决定任何改变是否要更新到DOM时被调用,作为一个优化实现比较this.props和nextProps,this.state和nextState,如果应该跳过更新,返回false。
3.componentWillUpdate:在更新发生前被立即调用,此时不能调用this.setState()
4.componentDidUpdate:在更新发生后被立即调用。(可以在DOM更新完之后,做一些收尾工作)
react的优化是基于shouldComponentUpdate的,该生命周期默认返回true,所以一旦prop或state有任何变化,都会引起重新render。
所以,可以得出react的性能优化就是围绕shouldComponentUpdate(SCU)方法来进行的,其优化无外乎两点:
缩短SCU方法的执行时间(或者直接不执行)
没必要的渲染,SCU就该返回false
shouldComponentUpdate:
react在每个组件生命周期更新的时候都会调用一个
shouldComponentUpdate(nextprops,nextState)函数,他的职责就是返回true,或者false,true表示需要更新,false就是表示不需要,默认值是返回true的,即便你没有显示的定义shouldComponentUpdate函数,他的返回值还是true。
根据渲染流程,首先会判断shouldcomponentUpdate是否需要更新,如果需要更新,则调用组件的render生成新的虚拟DOM,然后再与旧的虚拟DOM进行对比,如果对比一致就不更新,如果对比不同,则根据最小粒度改变去更新DOM,如果scu不需要更新,则直接保持不变,同时其子元素也保持不变。
注意:错误写法
{…this.props}(不能滥用,只传递component需要的props即可,传递的太多,或者层次传的太深,都会加重shuoldComponentUpdate,其里面的数据比较负担,因此一定要慎重使用
spread attributes(<Component {...props}/>))
::this.handleChange()(将该方法的bind一律置于constructor)
复杂的页面不要在一个组件中写完
尽量使用
const element定义
map里面添加key,并且key不要使用index(可变的)
尽量少的使用setTimeout或者不可控的refs,DOM操作
props和state的数据尽可能简单明了,扁平化。
使用return null而不是css的
display:none来控制节点的隐藏。保证同一时间页面的DOM节点尽可能的少。
3.性能分析工具
React特色工具:Perfreact官方提供一个插件react.addons.perf可以帮助我们分析组件的性能,以确认是否需要优化。
Perf 是react官方提供的性能分析工具。Perf最核心的方法莫过于
Perf.printWasted(measurements),该方法会列出那些没必要的组件渲染。很大程度上,React的性能优化就是干掉这些无谓的渲染。
操作:
打开console面板,先输入Perf.start()执行一些组件操作,引起数据变动,组件更新,然后输入Perf.stop()。(建议一次只执行一个操作,好进行分析)
然后在输入Perf.printInclusive将所有涉及到的组件render打印下来。(官方图片)
或者输入Perf.printWasted()查看不需要的浪费组件的render。
优化前:
优化后:
4.其它检测工具
react-perf-tool为react提供了一种可视化的性能检测方案,其同样是基于React.addons,但是使用图标来显示结果更加方便。1.PureRenderMixin(基于es5)
var PureRenderMixin = require('react-addons-pure-render-mixin'); React.createClass({ mixins: [PureRenderMixin], render: function() { return <div className={this.props.className}>foo</div>; } });
2.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>; } }
pureRender很简单,就是把穿进来的component的shouldComponentUpdate给重写掉,原来的shouldComponentUpdate,无论怎样都是
return true,现在不会这样了,我要用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,这就会导致在传入复杂的数据的情况下,优化会失效。
7.补充
react在15.3.0里面加入了react.PureComponent一个可继承的新的基础类,用来替换react-addons-pure-render-mixin,用法如下:class CounterButton extends React.PureComponent { constructor(props) { super(props); this.state = {count: 1}; } render() { return ( <button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button> ); } }
8.immutable.js
我们也可以在shouldComponentUpdate()中使用deepCopy和deepCompare来避免无必要的render(),但是deepCopy和deepCompare一般都是非常耗性能的。Immutable Date就是一旦创建,就不能再被更改的数据。对Immutable对象的任何修改或者添加删除操作都会返回一个新的Immutable对象。
Immutable实现的原理是Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变,同时为了避免deeepCopy把所有的节点都复制一遍带来的性能损耗,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性能优化总结
- React优化性能的经验教训
- React-Native性能优化点
- React性能检测和优化
- React性能优化——工具篇
- React组件性能优化
- React性能优化——代码篇
- React性能优化资料(文章)搜集整理(持续更新)
- 【react-router】从Link组件和a标签的区别说起,react-router如何实现导航并优化DOM性能?
- 【React自制全家桶】六、React性能优化(持续更新总结)
- React性能优化 PureComponent 使用指南
- React学习之进阶性能优化(十五)
- React-性能优化详解
- React 组件性能优化
- react-native 性能优化,处理卡顿
- React高级性能优化
- React-Native 性能优化 个人总结
- React性能优化心得
- React Native 性能优化之可取消的异步操作
- React性能优化——工具篇