详解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 相关文章推荐
新浪的图片新闻效果
Jan 13 Javascript
JQuery.uploadify 上传文件插件的使用详解 for ASP.NET
Jan 22 Javascript
jquery 元素相对定位代码
Oct 15 Javascript
PHP+jQuery实现随意拖动层并即时保存拖动位置
Apr 30 Javascript
JavaScript中getUTCMinutes()方法的使用详解
Jun 10 Javascript
JavaScript中setFullYear()方法的使用详解
Jun 11 Javascript
JavaScript模拟数组合并concat
Mar 06 Javascript
Javascript之面向对象--接口
Dec 02 Javascript
关于javascript事件响应的基础语法总结(必看篇)
Dec 26 Javascript
vue.js框架实现表单排序和分页效果
Aug 09 Javascript
AngularJS实现的base64编码与解码功能示例
May 17 Javascript
jQuery分组选择器简单用法示例
Apr 04 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
php过滤敏感词的示例
2014/03/31 PHP
3种方法轻松处理php开发中emoji表情的问题
2016/07/18 PHP
PHP 接入支付宝即时到账功能
2016/09/18 PHP
highchart数据源纵轴json内的值必须是int(详解)
2017/02/20 PHP
Laravel框架路由和控制器的绑定操作方法
2018/06/12 PHP
通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法
2020/04/02 PHP
PHP手机号码及邮箱正则表达式实例解析
2020/07/11 PHP
分享别人写的一个小型js框架
2007/08/13 Javascript
JS小功能(onmouseover实现选择月份)实例代码
2013/11/28 Javascript
详解JavaScript正则表达式之RegExp对象
2015/12/13 Javascript
在微信、支付宝、百度钱包实现点击返回按钮关闭当前页面和窗口的方法
2016/08/05 Javascript
浅析使用BootStrap TreeView插件实现灵活配置快递模板
2016/11/28 Javascript
jquery+ajax实现省市区三级联动 (封装和不封装两种方式)
2017/05/15 jQuery
详解Vue2.x-directive的学习笔记
2017/07/17 Javascript
浅谈jquery中ajax跨域提交的时候会有2次请求的问题
2017/11/10 jQuery
简单实现节流函数和防抖函数过程解析
2019/10/08 Javascript
详解react组件通讯方式(多种)
2020/05/06 Javascript
[40:06]DOTA2亚洲邀请赛 4.3 突围赛 Liquid vs VGJ.T 第一场
2018/04/04 DOTA
[01:23]2019完美世界全国高校联赛(春季赛)合肥全国总决赛
2019/06/10 DOTA
Python的多态性实例分析
2015/07/07 Python
关于Python中异常(Exception)的汇总
2017/01/18 Python
详解Python在七牛云平台的应用(一)
2017/12/05 Python
Python实现的json文件读取及中文乱码显示问题解决方法
2018/08/06 Python
python 将json数据提取转化为txt的方法
2018/10/26 Python
python绘制漏斗图步骤详解
2019/03/04 Python
python爬取百度贴吧前1000页内容(requests库面向对象思想实现)
2019/08/10 Python
浅析PyTorch中nn.Linear的使用
2019/08/18 Python
Python散点图与折线图绘制过程解析
2019/11/30 Python
matplotlib绘制鼠标的十字光标的实现(内置方式)
2021/01/06 Python
护理工作感言
2014/01/16 职场文书
小学生打架检讨书
2014/01/26 职场文书
局火灾防控工作方案
2014/05/25 职场文书
转让协议书
2015/01/27 职场文书
2016年教师学习廉政准则心得体会
2016/01/20 职场文书
Oracle数据库事务的开启与结束详解
2022/06/25 Oracle
Hive常用日期格式转换语法
2022/06/25 数据库