react性能优化

来源:互联网 发布:nginx点播系统 编辑:程序博客网 时间:2024/06/11 01:56

写在前面的话:

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


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;}
原创粉丝点击