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

在开发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与原生不同,混用的话容易出错。

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

(0)

相关推荐

  • 浅谈React前后端同构防止重复渲染

    什么叫前后端同构? 为了解决某些问题(比如SEO.提升渲染速度等)react 提供了2个方法在服务端生成一个HTML文本格式的字符串.在得到了这个HTML格式的字符串之后,通常会将其组装成一个页面直接返回给用户的浏览器. 到这里,服务端的活已经干完了,然后就是浏览器这边干活. 浏览器拿到HTML文本后,立刻进行渲染将内容呈现给用户.然后加载页面所需的 .js 文件,然后执行 JavaScript 脚本,然后开始初始化 react 组件---- 到这里问题就来了.react 初始化组件后会执行组件

  • 使用Node搭建reactSSR服务端渲染架构

    如题:本文所讲架构主要用到技术栈有: Node, Express, React, Mobx, webpack4, ES6, ES7, axios, ejs,  log4js, scss,echarts,ant desige SSR的概念 Server Slide Rendering,缩写为 ssr,即服务器端渲染,因为是后端出身,所以其实早就明白是怎么回事,只是没这个具体名词的概念罢了,这个词被频繁提起也是拜近年来前端飞速发展所赐,主要针对 SPA应用,目的大概有以下几个: 解决单页面应用的 S

  • React如何避免重渲染

    组件的重新渲染 我们可以在 React 组件中的 props 和 state 存放任何类型的数据,通过改变 props 和 state,去控制整个组件的状态.当 props 和 state 发生变化时,React 会重新渲染整个组件,组件重新渲染的过程可简化如下图: 译者之前对diff的理解是,对于一个改变 props 的组件,diff能自动计算出组件内部DOM树的不同,然后经过对比,找出真正变化的DOM节点,对变化部分进行渲染.这个是错误的理解,diff算法只是用来计算出改变状态或 props

  • 详解React 的几种条件渲染以及选择

    对于一个展示页面来讲, 通常有好几种展示状态(以列表页为例): 数据为空, 空页面 取数据时发生错误, 错误页面 数据正常 加载状态 针对以上三种情况, react渲染列表的时候要正确判断并渲染出相应的视图, 也就是条件渲染. 不同于vue的v-if, v-show等框架提供的api, react的条件渲染都是js原生的再加上一点点的hack. 比如react文档提到的. if/else, && 和三目等等. 当然上面的都是常用的一些方法, 但是也存在着各种问题, 比如条件分支过多的的事时

  • 浅谈react前后端同构渲染

    前后端同构渲染:当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互.然后,如果加载组件的过程需要向服务端发出Ajax请求等,客户端和服务端又进行了一次交互,这样,耗时相对较长.前后端同构渲染可以在页面初次加载时把所有地方渲染好一次性响应给客户端 实现方式:保证包管理工具和模块依赖方式一致 包管理工具-npm管理,保证前后端都使用同一个兼容包 模块依赖方式-webpack,保证前后端都采用commonjs的依赖方式,确保代码可以互相依赖 服务端如

  • 详解react服务端渲染(同构)的方法

    学习react也有一段时间了,使用react后首页渲染的速度与seo一直不理想.打算研究一下react神奇服务端渲染. react服务端渲染只能使用nodejs做服务端语言实现前后端同构,在后台对react组件进行解析并生成html字符串后返回视图页面. 后台为什么可以解析react组件?因为Node.js是一个Javascript运行环境,nodejs与javascript语法基本是相同的,所以nodejs可以正常解析react组件. 一.准备动作 1.安装nodejs与安装express 安

  • React服务端渲染(总结)

    一.前言 为什么需要服务端渲染?什么情况下进行服务端渲染?笔者认为,当我们要求渲染时间尽量快.页面响应速度快时(优点),才会采用服务器渲染,并且应该"按需"对页面进行渲染 --"首次加载/首屏".即服务端渲染的优势在于:由中间层( node端 )为客户端请求初始数据.并由node渲染页面.那客户端渲染和服务端渲染有什么差别?服务端渲染究竟快在哪里呢? 二.原因与思路 客户端渲染路线:1. 请求一个html -> 2. 服务端返回一个html -> 3.

  • 浅谈React 服务器端渲染的使用

    React 提供了两个方法 renderToString 和 renderToStaticMarkup 用来将组件(Virtual DOM)输出成 HTML 字符串,这是 React 服务器端渲染的基础,它移除了服务器端对于浏览器环境的依赖,所以让服务器端渲染变成了一件有吸引力的事情. 服务器端渲染除了要解决对浏览器环境的依赖,还要解决两个问题: 前后端可以共享代码 前后端路由可以统一处理 React 生态提供了很多选择方案,这里我们选用 Redux 和 react-router 来做说明. R

  • 从零开始最小实现react服务器渲染详解

    前言 最近在写 koa 的时候想到,如果我部分代码提供api,部分代码支持ssr,那我应该如何写呢?(不想拆成 2个服务的情况下) 而且最近写的项目里面也用过一些服务端渲染,如nuxt,自己也搭过next的项目,确实开发体验都非常友好,但是友好归友好,具体又是如何实现的呢,诸位有没有考虑过? 本着求真务实的折腾态度,选了react作为研究对象(主要是vue写的有点多,恶心了),那下面就简单就以最小成本写一个react的服务端渲染 demo 用到的技术栈 react 16 + webpack3 +

  • React学习笔记之列表渲染示例详解

    前言 本文主要给大家介绍了关于React列表渲染的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 示例详解: 列表渲染也很简单,利用map方法返回一个新的渲染列表即可,例如: const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li>{number}</li> ); ReactDOM.render( <ul>{listItems}<

  • 详解React+Koa实现服务端渲染(SSR)

    React是目前前端社区最流行的UI库之一,它的基于组件化的开发方式极大地提升了前端开发体验,React通过拆分一个大的应用至一个个小的组件,来使得我们的代码更加的可被重用,以及获得更好的可维护性,等等还有其他很多的优点... 通过React, 我们通常会开发一个单页应用(SPA),单页应用在浏览器端会比传统的网页有更好的用户体验,浏览器一般会拿到一个body为空的html,然后加载script指定的js, 当所有js加载完毕后,开始执行js, 最后再渲染到dom中, 在这个过程中,一般用户只能

随机推荐