zoukankan      html  css  js  c++  java
  • PureComponent

    前言        

    React15.3中新加了一个 PureComponent 类,PureComponent 也就是纯组件,取代其前身 PureRenderMixin ,

     PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数,节省了点代码。但是也有坑要注意

    原理

    当组件更新时,如果组件的 props 和 state 都没发生改变, render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。具体就是 React 自动帮我们做了一层浅比较:

    if (this._compositeType === CompositeTypes.PureClass) {
      shouldUpdate = !shallowEqual(prevProps, nextProps)
      || !shallowEqual(inst.state, nextState);
    }

    shallowEqual会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的。      

    实例

    在React Component的生命周期中,有一个shouldComponentUpdate方法。这个方法默认返回值是true。

    这意味着就算没有改变组件的props或者state,也会导致组件的重绘。这就经常导致组件因为不相关数据的改变导致重绘,这极大的降低了React的渲染效率。比如下面的例子中,任何options的变化,甚至是其他数据的变化都可能导致所有cell的重绘。

    
    {
       this.props.items.map(i =>
            <Cell data={i} option={this.props.options[i]} />
        )
    }

    重写shouldComponentUpdate

    为了避免这个问题,我们可以在Cell中重写shouldComponentUpdate方法,只在option发生改变时进行重绘。

    shouldComponentUpdate(nextProps, nextState) {
        if (this.props.option === nextProps.option) {
          return false;
        } else {
          return true;
        }
      }
    

    这样每个Cell只有在关联option发生变化时进行重绘。

    因为上面的情况十分通用,React创建了PureComponent组件创建了默认的shouldComponentUpdate行为。这个默认的shouldComponentUpdate行为会一一比较props和state中所有的属性,只有当其中任意一项发生改变是,才会进行重绘。

    需要注意的是,PureComponent使用浅比较判断组件是否需要重绘

    因此,下面对数据的修改并不会导致重绘(假设Table也是PureComponent)

      options.push(new Option())
      options.splice(0, 1)
      options[i].name = "Hello"
    

    这写例子都是在原对象上进行修改,浅比较只是比较指针的异同,会认为不需要进行重绘。

    为了避免出现这些问题,推荐使用immutable.js。immutable.js会在每次对原对象进行添加,删除,修改使返回新的对象实例。任何对数据的修改都会导致数据指针的变化。

    import immutable from 'immutable';
    const { Map, List } = immutable;
    
    componentDidMount(){
        const map1 = Map({a:1, b: 2, c: 3})
        const map2 = map1.set('b',50)
        console.log(map1.get("b"))   // 2
        console.log(map2.get("b"))   // 50
    
        const list1 = this.state.list;
        const list2 = list1.push(3,4,5) 
        const list3 = list2.unshift(0)
        const list4 = list1.concat(list2,list3)
        console.log(list1.size) //2
        console.log(list2.size) //5
        console.log(list3.size) //6
        console.log(list4.size) //13
    }

    使用immutable的优点

    第一点,丰富的语法糖,有了ImmutableJS,代码中就不会再出现如下内容:

    //为了在不污染原对象的前提下增加新的KV
    var state = Object.assign({}, state, {
    	key: value
    });
    
    //为了在不污染原数组的前提下插入新元素
    var state = [
    	...state.slice(0, index),
    	insertData,
    	...state.slice(index + 1)
    ];

    有时候,为了保证reducer在处理 state 的时候不会改变传入的 state,就要写大量的上述代码。但是有了 Immutable.js,你就可以这么写:

    var state = state.set('key', value);
    
    var state = state.splice(index, 1, value);

    第二点,性能的提升。由于 immutable 内部使用了 Trie 数据结构来存储,只要两个对象的 hashCode 相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。这对我们在进行具体渲染过程中的性能优化非常有用。

    更深层的使用请参考http://react-china.org/t/react-redux-immutablejs/9948

    使用PureComponent的坑

    1、Literal Array与Literal Object 数组字面量 对象字面量

    {
       this.props.items.map(i =>
          <Cell data={i} options={this.props.options || []} />
       )
    }
    

    若options为空,则会使用[]。[]每次会生成新的Array,因此导致Cell每次的props都不一样,导致需要重绘。解决方法如下:

    const default = [];
    {
      this.props.items.map(i =>
        <Cell data={i} options={this.props.options || default} />
      )
    }
    

    2、内联函数

    函数也经常作为props传递,由于每次需要为内联函数创建一个新的实例,所以每次function都会指向不同的内存地址。比如:

    render() {
         <MyInput onChange={e => this.props.update(e.target.value)} />;
    }

    以及:

    update(e) {
         this.props.update(e.target.value);
    }
    render() {
         return <MyInput onChange={this.update.bind(this)} />;
    }

    注意第二个例子也会导致创建新的函数实例。为了解决这个问题,需要提前绑定this指针:

    constructor(props) {
        super(props);
        this.update = this.update.bind(this);
      }
      update(e) {
        this.props.update(e.target.value);
      }
      render() {
        return <MyInput onChange={this.update} />;
      }
    

    或者使用箭头函数来解决

    constructor(props) {
        super(props);
      }
      update=(e)=> {
        this.props.update(e.target.value);
      }
      render() {
        return <MyInput onChange={this.update} />;
      }

    与 shouldComponentUpdate 共存

    如果 PureComponent 里有 shouldComponentUpdate 函数的话,直接使用 shouldComponentUpdate的结果作为是否更新的依据,没有 shouldComponentUpdate 函数的话,才会去判断是不是 PureComponent ,是的话再去做 shallowEqual 浅比较。

    // 这个变量用来控制组件是否需要更新
    var shouldUpdate = true;
    // inst 是组件实例
    if (inst.shouldComponentUpdate) {
      shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
    } else {
      if (this._compositeType === CompositeType.PureClass) {
        shouldUpdate = !shallowEqual(prevProps, nextProps) ||
          !shallowEqual(inst.state, nextState);
      }
    }

    老版本兼容写法

    import React { PureComponent, Component } from 'react';
    
    class Foo extends (PureComponent || Component) {
      //...
    }

    这样在老版本的 React 里也不会挂掉。

    PureComponent 真正起作用的,只是在一些纯展示组件上,复杂组件我认为用了反而会更复杂,该更新的时候不更新,容易造成更多bug,源码里shallowEqual 那一关就过不了。

    参考:http://www.wulv.site/2017-05-31/react-purecomponent.html

               https://www.jianshu.com/p/33cda0dc316a 

  • 相关阅读:
    Linux ,Ubuntu 分区建议大小
    [No000019A]【波浪理论精典教学课程】
    CS(计算机科学)知识体
    如何量化考核技术人的KPI?
    Linux系统的命令应该如何记?
    数学和物理太难?这些 GIF 让你秒懂抽象概念
    加密数字货币的 7 个致命悖论
    量子力学中,全体自然数之和是负十二分之一,物理意义是什么?
    30岁左右的人,来谈谈你犯了哪些错误?
    一篇文章学懂Shell脚本,最简明的教程在这里
  • 原文地址:https://www.cnblogs.com/gwf93/p/10295112.html
Copyright © 2011-2022 走看看