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


Posted in Javascript onDecember 11, 2018

在开发React组件的过程中,我们经常会遇到这个问题:什么情况下组件会重新渲染?

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

以下几个问题同样值得我们思考:

setState()函数在任何情况下都会导致组件重渲染吗?如果setState中的state没有发生改变呢?

如果state和从父组件传过来的props都没变化,那他就一定不会发生重渲染吗?

首先,我们来解决这两个问题

没有导致state的值发生变化的this.setState()是否会导致重渲染  --- 

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,但是组件并没有发生任何变化!!!

这样的结果不是我们想要的,如何阻止组件的重渲染呢?这时我们想到了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呢?

四种方法:

1、ES6的扩展语法Object.assign()

2、深拷贝/浅拷贝或利用JSON.parse(JSON.stringify(data))相当于深拷贝,但使用受一定限制

3、引入immutable.js react官方推荐的第三方库

4、继承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 不存在复制代码

优点:

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

缺点:

  • 需要重新学习api
  • 资源包大小增加(源码5000行左右)
  • 容易与原生对象混淆:由于api与原生不同,混用的话容易出错。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 实现??打印?理
Apr 28 Javascript
JQuery 学习笔记 选择器之五
Jul 23 Javascript
再谈javascript 动态添加样式规则 W3C校检
Dec 25 Javascript
js 如何实现对数据库的增删改查
Nov 23 Javascript
jquery实现textarea输入字符控制(仿微博输入控制字符)
Apr 26 Javascript
jquery放大镜效果超漂亮噢
Nov 15 Javascript
JavaScript获取当前网页最后修改时间的方法
Apr 03 Javascript
理解javascript模块化
Mar 28 Javascript
BootStrap Validator使用注意事项(必看篇)
Sep 28 Javascript
jQuery的中 is(':visible') 解析及用法(必看)
Feb 12 Javascript
JS全角与半角转化实例(分享)
Jul 04 Javascript
JS鼠标3次点击事件实现代码及扩展思路
Sep 12 Javascript
jQuery实现数字自动增加或者减少的动画效果示例
Dec 11 #jQuery
利用jsonp解决js读取本地json跨域的问题
Dec 11 #Javascript
实现Vue的markdown文档可以在线运行的方法示例
Dec 11 #Javascript
JS中使用new Option()实现时间联动效果
Dec 10 #Javascript
vue刷新页面时去闪烁提升用户体验效果的实现方法
Dec 10 #Javascript
移动端如何用下拉刷新的方式实现上拉加载
Dec 10 #Javascript
vue+Element-ui实现分页效果实例代码详解
Dec 10 #Javascript
You might like
如何正确理解PHP的错误信息
2006/10/09 PHP
基于PHP后台的Android新闻浏览客户端
2016/05/23 PHP
php实现的http请求封装示例
2016/11/08 PHP
PHP __call()方法实现委托示例
2019/05/20 PHP
jQuery中text() val()和html()的区别实例详解
2016/06/28 Javascript
AngularJS表单验证中级篇(3)
2016/09/28 Javascript
浅谈EasyUi ComBotree树修改 父节点选择的问题
2016/11/07 Javascript
javascript中的后退和刷新实现方法
2016/11/10 Javascript
Javascript中的 “&amp;” 和 “|” 详解
2017/02/02 Javascript
javascript事件的传播基础实例讲解(35)
2017/02/14 Javascript
Vue代码分割懒加载的实现方法
2017/11/23 Javascript
把vue-router和express项目部署到服务器的方法
2018/02/21 Javascript
微信小程序项目总结之点赞 删除列表 分享功能
2018/06/25 Javascript
微信小程序使用二次贝塞尔曲线画波浪
2018/12/25 Javascript
详解VS Code使用之Vue工程配置format代码格式化
2019/03/20 Javascript
微信小程序实现文件预览
2020/10/22 Javascript
[04:32]玩具屠夫中文语音节选
2020/08/23 DOTA
[05:49]DOTA2-DPC中国联赛 正赛 Elephant vs LBZS 选手采访
2021/03/11 DOTA
python使用电子邮件模块smtplib的方法
2016/08/28 Python
Python变量和数据类型详解
2017/02/15 Python
Python定时器实例代码
2017/11/01 Python
numpy返回array中元素的index方法
2018/06/27 Python
wxPython多个窗口的基本结构
2019/11/19 Python
Python print不能立即打印的解决方式
2020/02/19 Python
完美解决TensorFlow和Keras大数据量内存溢出的问题
2020/07/03 Python
Python列表嵌套常见坑点及解决方案
2020/09/30 Python
HTML5 新标签全部总汇(推荐)
2016/06/13 HTML / CSS
受外贸欢迎的美国主机:BlueHost
2017/05/16 全球购物
土耳其时尚购物网站:Morhipo
2017/09/04 全球购物
中国高端家电购物商城:顺电
2018/03/04 全球购物
澳大利亚女性快速时尚零售商:Ally Fashion
2018/04/25 全球购物
英国奢侈皮具品牌:Aspinal of London
2018/09/02 全球购物
简述进程的启动、终止的方式以及如何进行进程的查看
2014/02/20 面试题
企业给企业的表扬信
2014/01/13 职场文书
2015年行政执法工作总结
2015/05/23 职场文书
当幸福来敲门英文观后感
2015/06/01 职场文书