react阻止无效重渲染的多种方式

在开发React组件的过程当中,咱们常常会遇到这个问题:什么状况下组件会从新渲染?html

  当内部data发生改变,state发生改变(经过调用this.setState()) 以及父组件传过来的props发生改变时,会致使组件从新渲染。react


如下几个问题一样值得咱们思考:
  setState()函数在任何状况下都会致使组件重渲染吗?若是setState中的state没有发生改变呢?
  若是state和从父组件传过来的props都没变化,那他就必定不会发生重渲染吗?编程

首先,咱们来解决这两个问题
  没有致使state的值发生变化的this.setState()是否会致使重渲染      ---  会 redux

import React from 'react'
class Test extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      Number:1//设state中Number值为1
    }
  }
  //这里调用了setState可是并无改变setState中的值
  handleClick = () => {
     const preNumber = this.state.Number
     this.setState({
        Number:this.state.Number
     })
  }
  render(){
    //当render函数被调用时,打印当前的Number
    console.log(this.state.Number)
    return(<h1 onClick = {this.handleClick}>
             {this.state.Number}
           </h1>)
  }
}

从控制台的打印结果能够看出:共打印了15次1,可是组件并无发生任何变化!!!api

 这样的结果不是咱们想要的,如何阻止组件的重渲染呢?这时咱们想到了React的一个生命周期钩子 shouldComponentUpdate数组

 

react生命周期中有这样一个钩子,叫shouldComponentUpdate函数,是重渲染时render()函数调用前被调用的函数,数据结构

  两个参数 nextProps和nextState ,分别表示下一个props和state的值。函数式编程

  当函数返回false时,阻止接下来的render()函数的调用,阻止组件重渲染,返回true时,组件照常渲染函数

 

 //加入shouldComponentUpdate钩子
//在render函数调用前判断:若是先后state中Number不变,经过return false阻止render调用 shouldComponentUpdate(nextProps,nextState){ if(nextState.Number == this.state.Number){ return false } }
加入上述代码后,打开控制台,点击按钮,仍是白白的,说明无效的重渲染被咱们阻止了

 

第二个问题,组件的state和从父组件传递过来的props都没改变,组件还会重渲染吗 ---可能
  一样能够经过shouldComponentUpdate钩子进行阻止性能

因此说,先后不改变state的值的setState和无数据交换的父组件的重渲染都会致使组件的重渲染,但咱们能够经过shouldComponentUpdate来阻止这两种状况
shouldComponentUpdate并非完美的,只能阻止扁平的对象

 

nextState.Number == this.state.Number
若是调用层次比较深
nextState.NumberObject.number == this.state.NumberObject.number
Number 是一个数字变量
NumberObject是一个对象
数字变量(number类型)和对象(Object)类型的内存存储机制不一样
这时候,由于二者都指向堆中的同一个对象,因此一直都是true  shouldComponentUpdate失效了
js变量分为基本类型的变量和引用类型的变量

  对于number,string,boolean,undefined,null这些基本类型变量,值存在栈中
  对于object,Array,function这些引用类型变量,引用存在栈中,而不一样的引用却能够指向堆内存中的同一个对象

 那么,问题就来了

怎么样才能取到不一样的NumberObject呢?
四种方法:
  一、ES6的扩展语法Object.assign()
  二、深拷贝/浅拷贝或利用JSON.parse(JSON.stringify(data))至关于深拷贝,但使用受必定限制
  三、引入immutable.js react官方推荐的第三方库
  四、继承react的PureComponent组件(代替Component)

 

在js中,引用类型的数据,优势在于频繁的操做数据都是在原对象的基础上修改,不会建立新对象,从而能够有效的利用内存,不会浪费内存,这种特性称为mutable(可变),但偏偏它的优势也是它的缺点,太过于灵活多变在复杂数据的场景下也形成了它的不可控性,假设一个对象在多处用到,在某一处不当心修改了数据,其余地方很难预见到数据是如何改变的,针对这种问题的解决方法,通常就像刚才的例子,会想复制一个新对象,再在新对象上作修改,这无疑会形成更多的性能问题以及内存浪费。
  为了解决这种问题,出现了immutable对象,每次修改immutable对象都会建立一个新的不可变对象,而老的对象不会改变。

immutable.js主要有三大特性:
Persistent data structure (持久化数据结构)
structural sharing (结构共享)
support lazy operation (惰性操做)

 

Immutable Data 就是一旦建立,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操做都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据建立新数据时,要保证旧数据同时可用且不变。同时为了不 deepCopy 把全部节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即若是对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享

三个最重要的数据结构: Map List Set
Map:键值对集合,对应于 Object,ES6 也有专门的 Map 对象
List:有序可重复的列表,对应于 Array
Set:无序且不可重复的列表

 

//Map()  原生object转Map对象 (只会转换第一层,注意和fromJS区别)
immutable.Map({name:'danny', age:18})

//List()  原生array转List对象 (只会转换第一层,注意和fromJS区别)
immutable.List([1,2,3,4,5])

//fromJS()   原生js转immutable对象  (深度转换,会将内部嵌套的对象和数组所有转成immutable)
immutable.fromJS([1,2,3,4,5])    //将原生array  --> List
immutable.fromJS({name:'danny', age:18})   //将原生object  --> Map

//toJS()  immutable对象转原生js  (深度转换,会将内部嵌套的Map和List所有转换成原生js)
immutableData.toJS();

//查看List或者map大小  
immutableData.size  或者 immutableData.count()

// is()   判断两个immutable对象是否相等
immutable.is(imA, imB);

//merge()  对象合并
var imA = immutable.fromJS({a:1,b:2});
var imA = immutable.fromJS({c:3});
var imC = imA.merge(imB);
console.log(imC.toJS())  //{a:1,b:2,c:3}
对于两个同样的数据,只有经过equals进行比较才是相等的  ==  ===都不行
若是 某个是另外一个克隆出来的,那么所有都相等
push添加 unshift在头部添加 concat组合  返回的是新数据,而不是数据的长度
//增删改查(全部操做都会返回新的值,不会修改原来值)
var immutableData = immutable.fromJS({
    a:1,
    b:2,
    c:{
        d:3
    }
});
var data1 = immutableData.get('a') //  data1 = 1  
var data2 = immutableData.getIn(['c', 'd']) // data2 = 3   getIn用于深层结构访问
var data3 = immutableData.set('a' , 2);   // data3中的 a = 2
var data4 = immutableData.setIn(['c', 'd'], 4);   //data4中的 d = 4
var data5 = immutableData.update('a',function(x){return x+4})  //data5中的 a = 5
var data6 = immutableData.updateIn(['c', 'd'],function(x){return x+4}) //data6中的 d = 7
var data7 = immutableData.delete('a')   //data7中的 a 不存在
var data8 = immutableData.deleteIn(['c', 'd'])   //data8中的 d 不存在复制代码

 

优势:
  下降immutable带来的复杂度
  节省内存
  历史追溯性(时间旅行):时间旅行指的是,每时每刻的值都被保留了,想回退到哪一步只要简单的将数据取出就行,想一下若是如今页面有个撤销的操做,撤销前的数据被保留了,只须要取出就行,这个特性在redux或者flux中特别有用
  拥抱函数式编程:immutable原本就是函数式编程的概念,纯函数式编程的特色就是,只要输入一致,输出必然一致,相比于面向对象,这样开发组件和调试更方便


缺点:
  须要从新学习api
  资源包大小增长(源码5000行左右)
  容易与原生对象混淆:因为api与原生不一样,混用的话容易出错。

 码字不易,引用请注明出处!  ---拈花煮酒

http://www.cnblogs.com/luxiaot/p/10097572.html

相关文章
相关标签/搜索