您的位置:首页 > Web前端 > React

react性能优化

2017-08-28 15:45 381 查看
写在前面的话:

要想解决问题,首先得找到问题的根源,所以,说起性能分析,还是要从其生命周期和渲染机制说起。

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特色工具:Perf

react官方提供一个插件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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息