【React】—组件性能优化

每个项目产品都要加埋点,加500行埋点是不是会占用你一两天的时间而且很容易犯错,想只用一小时准确加完这500行埋点剩下一天喝茶聊天么?来试试这520web工具, 高效加埋点,目前我们公司100号前端都在用,因为很好用,所以很自然普及开来了,推荐给大家吧

http://www.520webtool.com/

自己开发所以免费,埋点越多越能节约时间,点两下埋点就加上了,还不会犯错,里面有使用视频,反正免费 😄

一、优化原理

改写react生命周期shouldComponentUpdate,使其在需要重新渲染当前组件时返回true,否则返回false。不再全部返回true。

二、主流优化方式

1.react官方解决方案

原理:重写默认的shouldComponentUpdate,将旧props、state与新props、state逐个进行浅比较(形如:this.props.option === nextProps.option ?  false : true),如果全部相同,返回false,如果有不同,返回true。

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 SampleComponent extends Component {

    render() {

        return (

 

            <div className={this.props.className}>foo</div>

 

        )

    }

}

react 15.3.0+写法(用来替换react-addons-pure-render-mixin):

class SampleComponent extends React.PureComponent{

    render(){

        return(

            <div className={this.props.className}>foo</div>

        )

    }

}

*上述方案存在问题(浅比较的问题):

(1)某些props、state值未改变的情况,返回true,例如:

 <Cell options={this.props.options || [ ]} />

当this.props.options == false时,options=[ ]。当父组件两次渲染,this.props.options一直 == false,对于Cell组件来说,options没有改变,不需要重新渲染。但Cell的shouldComponentUpdate中进行的是浅比较,由于[ ] !== [ ],所以,this.props.options === nextProps.options为false,shouldComponentUpdate会返回true,Cell将进行重新渲染。

解决方法如下:

const default = [ ];

<Cell options={this.props.options || default} />

(2)某些props、state值改变的情况,返回false,例如:

handleClick() {

    let {items} = this.state;

    items.push('new-item') ;

    this.setState({ items });

}

render() {

    return (

        <div>

            <button onClick={this.handleClick} />

            <ItemList items={this.state.items} />

        </div>
    )

}

如果ItemList是纯组件(PureComponent),那么这时它是不会被渲染的。因为尽管this.state.items的值发生了改变,但是它仍然指向同一个对象的引用。

 


 

2.Immutable

原理:Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。

Immutable Data就是一旦被创建,就不能再更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。同时,为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

【在react中使用immutable】

改变shouldComponentUpdate的重新渲染规则

(1)防止每次setState或传递props,即使state和props的值没有发生改变也重新渲染组件,带来无谓的性能消耗

(2)防止浅比较带来的比较误差问题,以及深比较带来的性能消耗问题。

import { is, fromJS } from 'immutable';

constructor(){

    this.state = {

        person: fromJS({

                name: 'xuxuan',

                age: 12 

        });

   }

}

componentDidMount(){

    this.setState({

        person: this.state.person.update('name', v => v + 'update')

    });

}

shouldComponentUpdate: (nextProps, nextState) => {

    return !(this.props === nextProps || is(this.props, nextProps))

                ||

              !(this.state === nextState || is(this.state, nextState));

}

!!注意】state 本身必须是普通对象,但是里面的值可以是 immutable 的数据。

this.state和nextState是两个普通对象,通过is方法判断二者的值是否相同。

 

从setState到re-render整个过程(个人理解)

(1)setState中设置person的name值,由于person是immutable的,因此,这里会开辟一块新的内存,存储设置后的name(即使name的值没有真的发生改变,只要对immutable数据进行了设置操作,就会生成一个全新的对象),同时,开辟新内存,存储受name影响的immutable的各级父节点,这里只有person。而,其余未受影响的节点,将进行内存共享,如这里的age。由此,形成了新的state对象,即:nextState。

(2)setState(nextState)操作触发shouldComponentUpdate生命周期执行。

(3)shouldComponentUpdate中使用immutable.js的is方法对比两个对象。

        这里,is采用hashCodevalueOf对比两个对象是否相同:

        *当对象的值没有改变,is返回true,组件不重新渲染。依然存储旧的state(nextState大概会被销毁吧)

        *当对象的值发生改变,is返回false,组件重新渲染。nextState替换state(state就不能再访问了,但它的immutable的属性应该还可以)

(4)当需要重新渲染的时候,对于state来说,就是用新的nextState替换旧的state。由于state是普通对象,因此可以被更改,被替换。

 

【关于is方法】

Immutable的is比较的是这个对象hashCodevalueOf,只要两个对象的hashCode相等,值就是相同的,避免了深度遍历,提高了性能。

扩展:

hashCode的比较是将两个对象String化(eg:{a:111} —> '{a:111}')后,比较两个字符串对应位置字符的charCode是否相同,完全相同则认为是两个相同的对象。

 

使用 Immutable 后,如下图,当红色节点的 state 属性值变化后,

 

Immutable数据树结构变化示意图

【注】

整棵树就是一个Immutable的对象。

当红色节点发生改变,为这个节点及其父节点开辟新的内存,存储新数据,其他蓝色节点不变,共享之前的内存。

 

immutable.js框架是非常好的Immutable库,其他可用api,详见官方文档。

使用原则:

由于侵入性较强,新项目引入比较容易,老项目迁移需要谨慎评估迁移成本。对于一些提供给外部使用的公共组件,最好不要把Immutable对象直接暴露在对外的接口中。

 

【!!Mark】

个人理解部分,如有理解偏差,请指正,感谢。

 

 



作者:南慕瑶
链接:https://www.jianshu.com/p/4dfdf0a412ed
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值