详解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 相关文章推荐
浅谈Sizzle的“编译原理”
Apr 14 Javascript
jQuery动态背景图片效果实现方法
Jul 03 Javascript
在AngularJS中使用jQuery的zTree插件的方法
Apr 21 Javascript
基于原生JS实现图片裁剪
Aug 01 Javascript
jQuery 获取页面li数组并删除不在数组中的key
Aug 02 Javascript
jQuery中animate()的使用方法及解决$(”body“).animate({“scrollTop”:top})不被Firefox支持的问题
Apr 04 jQuery
label+input实现按钮开关切换效果的实例
Aug 16 Javascript
vue中v-for通过动态绑定class实现触发效果
Dec 06 Javascript
vue.js指令v-for使用以及下标索引的获取
Jan 31 Javascript
JavaScript canvas基于数组生成柱状图代码实例
Mar 06 Javascript
ant-design-vue中的select选择器,对输入值的进行筛选操作
Oct 24 Javascript
jQuery实现手风琴特效
Jan 11 jQuery
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
全国FM电台频率大全 - 11 浙江省
2020/03/11 无线电
PHP计划任务、定时执行任务的实现代码
2011/04/23 PHP
解析用PHP实现var_export的详细介绍
2013/06/20 PHP
php操作mysql数据库的基本类代码
2014/02/25 PHP
php数组索引的Key加引号和不加引号的区别
2014/08/19 PHP
ThinkPHP结合AjaxFileUploader实现无刷新文件上传的方法
2014/10/29 PHP
php中有关合并某一字段键值相同的数组合并的改进
2015/03/10 PHP
前淘宝前端开发工程师阿当的PPT中有JS技术理念问题
2010/01/15 Javascript
兼容IE与firefox火狐的回车事件(js与jquery)
2010/10/20 Javascript
Jquery性能优化详解
2014/05/15 Javascript
JavaScript中Function详解
2015/02/27 Javascript
javascript常见数字进制转换实例分析
2016/04/21 Javascript
基于Bootstrap的网页设计实例
2017/03/01 Javascript
nodejs个人博客开发第二步 入口文件
2017/04/12 NodeJs
EasyUI框架 使用Ajax提交注册信息的实现代码
2017/09/27 Javascript
JS基于对象的特性实现去除数组中重复项功能详解
2017/11/17 Javascript
如何在vue里面优雅的解决跨域(路由冲突问题)
2019/01/20 Javascript
nodejs微信开发之接入指南
2019/03/17 NodeJs
js实现简单五子棋游戏
2020/05/28 Javascript
jenkins自动构建发布vue项目的方法步骤
2021/01/04 Vue.js
Nest.js 授权验证的方法示例
2021/02/22 Javascript
python之wxPython应用实例
2014/09/28 Python
Python with语句上下文管理器两种实现方法分析
2018/02/09 Python
python leetcode 字符串相乘实例详解
2018/09/03 Python
python中元组的用法整理
2020/06/15 Python
使用CSS3实现圆角,阴影,透明
2014/12/23 HTML / CSS
HTML5图片层叠的实现示例
2020/07/07 HTML / CSS
匈牙利墨盒和碳粉购买网站:CDRmarket
2018/04/14 全球购物
加拿大领先家居家具网上购物:Aosom.ca
2020/05/27 全球购物
升职自荐信
2013/11/28 职场文书
政府绩效管理实施方案
2014/05/04 职场文书
投资建议书模板
2014/05/12 职场文书
商场租赁意向书
2014/07/30 职场文书
2014年党员自我评议(5篇)
2014/09/12 职场文书
2015年度女工工作总结
2015/10/22 职场文书
详解Redis在SpringBoot工程中的综合应用
2021/10/16 Redis