详解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 基础篇(一)
Mar 30 Javascript
javascript使用输出语句实现网页特效代码
Aug 06 Javascript
基于jquery实现表格内容筛选功能实例解析
May 09 Javascript
Vue.js每天必学之计算属性computed与$watch
Sep 05 Javascript
Angular页面间切换及传值的4种方法
Nov 04 Javascript
vue.js学习笔记之v-bind和v-on解析
May 03 Javascript
js正则相关知识点专题
May 10 Javascript
layui框架table 数据表格的方法级渲染详解
Aug 19 Javascript
详解基于vue-cli3.0如何构建功能完善的前端架子
Oct 09 Javascript
JS实现图片拖拽交换效果
Nov 30 Javascript
深入koa-bodyparser原理解析
Jan 16 Javascript
vue仿淘宝滑动验证码功能(样式模仿)
Dec 10 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
非常不错的MySQL优化的8条经验
2008/03/24 PHP
通过实例解析PHP数据类型转换方法
2020/07/11 PHP
jquery获取input表单值的代码
2010/04/19 Javascript
jQuery使用hide方法隐藏页面上指定元素的方法
2015/03/30 Javascript
Javascript中3个需要注意的运算符
2015/04/02 Javascript
jQuery实现返回顶部效果的方法
2015/05/29 Javascript
js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)
2015/11/09 Javascript
jquery层级选择器的实现(匹配后代元素div)
2016/09/05 Javascript
angular ngClick阻止冒泡使用默认行为的方法
2016/11/03 Javascript
Vue中保存用户登录状态实例代码
2017/06/07 Javascript
input type=file 选择图片并且实现预览效果的实例
2017/10/26 Javascript
「中高级前端面试」JavaScript手写代码无敌秘籍(推荐)
2019/04/08 Javascript
微信小程序实现Session功能及无法获取session问题的解决方法
2019/05/07 Javascript
vue自定义标签和单页面多路由的实现代码
2020/05/03 Javascript
jQuery 添加元素和删除元素的方法
2020/07/15 jQuery
pydev使用wxpython找不到路径的解决方法
2013/02/10 Python
Python实现的Google IP 可用性检测脚本
2015/04/23 Python
Django自定义插件实现网站登录验证码功能
2017/04/19 Python
python通过链接抓取网站详解
2019/11/20 Python
关于Pytorch的MLP模块实现方式
2020/01/07 Python
使用Python爬虫库requests发送请求、传递URL参数、定制headers
2020/01/25 Python
python使用matplotlib绘制折线图的示例代码
2020/09/22 Python
Python3.9最新版下载与安装图文教程详解(Windows系统为例)
2020/11/28 Python
基于HTML5+tracking.js实现刷脸支付功能
2020/04/16 HTML / CSS
Sephora丝芙兰澳洲官方网站:国际知名化妆品购物
2016/10/27 全球购物
同程旅游英文网站:LY.com
2018/11/13 全球购物
全球精选男装和家居用品:Article
2020/04/13 全球购物
幼儿园标语大全
2014/06/19 职场文书
县委常委班子专题民主生活会查摆问题及整改措施
2014/09/27 职场文书
2014年数学教研组工作总结
2014/12/06 职场文书
师德师风个人总结
2015/02/06 职场文书
导游词之沈阳清昭陵
2019/12/28 职场文书
详解用Python把PDF转为Word方法总结
2021/04/27 Python
vue点击弹窗自动触发点击事件的解决办法(模拟场景)
2021/05/25 Vue.js
java调用Restful接口的三种方法
2021/08/23 Java/Android
php访问对象中的成员的实例方法
2021/11/17 PHP