react性能优化之shouldComponentUpdate的原理剖析

news/2024/4/24 5:23:51/

shouldComponentUpdate原理讲解

  • shouldComponentUpdate是干什么的
  • 怎么使state更新而render函数不执行呢?
  • 使用shouldComponentUpdate完成性能优化
  • 当组件的state没有变化,props也没有变化,render函数可能执行吗?
  • pureComponent的基本用法

shouldComponentUpdate是干什么的

话不多说直接看一个简单的react实例

	import { render } from "react-dom";import React from "react";import { useState } from "react";class testPage extends React.Component {constructor(props) {super(props);this.state = {number: 1,};}changeState = () => {this.setState({number: 2,});};render() {console.log("render函数执行了");return (<><div>这里是number{this.state.number}</div><button onClick={this.changeState}>点我改变number</button></>);}}export default testPage;

那么既然你都看到了react的性能优化篇,那么我已经默认你对react基础有一定的了解,react的语法我不过多赘述,这个小demo很简单,页面上显示了number(初始值为1),然后点击按钮 number变成 2。此时来看看render函数打印了几次。

在这里插入图片描述
毫无意外的,执行两次。

那么此时问题来了

怎么使state更新而render函数不执行呢?

只需要加上这三行代码即可

shouldComponentUpdate(nextProps, nextState) {return false;}

同时,也许你会疑惑,怎么还有这种需求,数据更新,视图不更新,那我还用react干嘛?
大部分情况下确实是这样,但是此时你考虑一下特殊情况如下。我将按钮点击触发的函数改一改

	 this.setState({number: 1,  // 之前是2 我现在改成了1});

number初始值是1,我改变之后还是1,那么此时会不会执行render函数呢?
来看效果
在这里插入图片描述
意料之外又在情理之中,render函数还是执行了。那么这种情况就是我们所说的特殊情况。在一整个项目中肯定会涉及到大量这样state没变化但是render函数执行的情况。那么此时shouldComponentUpdate就排上用场了。

使用shouldComponentUpdate完成性能优化

简单的改写一下shouldComponentUpdate钩子就能完成这个基本的需求啦。

shouldComponentUpdate(nextProps, nextState) {if (nextState.number === this.state.number) {// 说明state里面的值并没有改return false;} else {return true;}}

但是我们只考虑到了触发render函数的一种情况哦!props的改变也会触发render函数执行哦!现在思考另外一个问题。

当组件的state没有变化,props也没有变化,render函数可能执行吗?

如果你的答案是“NO”,那么看下面这个例子。

	import { render } from "react-dom";
import React from "react";
import { useState } from "react";class testPage extends React.Component {constructor(props) {super(props);this.state = {numberArray: [1, 2, 3],};}//点击后使numberArray中数组下标为index的数字值加一,重渲染对应的Son组件handleClick = (index) => {let preNumberArray = this.state.numberArray;preNumberArray[index] += 1;this.setState({numberArray: preNumberArray,});};render() {return (<div style={{ margin: 30 }}>{this.state.numberArray.map((number, key) => {return (<Sonkey={key}index={key}number={number}handleClick={this.handleClick}/>);})}</div>);}
}class Son extends React.Component {render() {const { index, number, handleClick } = this.props;//在每次渲染子组件时,打印该子组件的数字内容console.log(number);return <h1 onClick={() => handleClick(index)}>{number}</h1>;}
}export default testPage;

同样的,我也不会对这个函数的语法进行分析,主要功能就是页面展示1,2,3,点击之后数字+1。那么此时你再想想此章节title中的问题的答案。如果组件的props和state没有变化,但是它的父组件render执行了,那么也一并会触发子组件的执行!看实例。
在这里插入图片描述
开始是1,2,3没错,此时我们点击3之后,视图变成了
在这里插入图片描述
此时渲染1和2的两个son组件,它们的props是没有变化的,它们的states也是没有变化的,但是它们的render函数还是执行了。
对此,我们依然可以故技重施。 改写shouldComponentUpdate组件。
在这里插入图片描述
优化完成。那么新的问题又出现了。
思考下面这个例子。

import { render } from "react-dom";
import React from "react";
import { useState } from "react";class testPage extends React.Component {constructor(props) {super(props);this.state = {numberArray: [{ number: 1 }, { number: 2 }, { number: 3 }],};}//点击后使numberArray中数组下标为index的数字值加一,重渲染对应的Son组件handleClick = (index) => {let preNumberArray = this.state.numberArray;preNumberArray[index].number += 1;this.setState({numberArray: preNumberArray,});};render() {return (<div style={{ margin: 30 }}>{this.state.numberArray.map((item, key) => {return (<Sonkey={key}index={key}numberObject={item}handleClick={this.handleClick}/>);})}</div>);}
}class Son extends React.Component {render() {const { numberObject, index, handleClick } = this.props;//在每次渲染子组件时,打印该子组件的数字内容console.log(numberObject.number);return <h1 onClick={() => handleClick(index)}>{numberObject.number}</h1>;}
}export default testPage;

当你认真看完之后也许会发现我在垂死挣扎,就是把数组[1,2,3]换成了对象形式[{number:1},{number:2},{number:3}],然后你自信满满的在son组件中加了如下代码

shouldComponentUpdate(nextProps, nextState) {if (nextProps.numberObject.number == this.props.numberObject.number) {return false;}return true;}

然后发现
在这里插入图片描述
不论你怎么点击,页面都不会再有任何反应。所以不论react,vue多么牛逼,它们最终还是用js写的。又回到了js基础中的基础。基本类型和引用对象类型。我们用一张小图来分析它们之间的关系
123123
其实在图中可以看出,由于使用的是引用对象而且指向的是同一个内存区域,所以在数据更新的时候,所以在作比较的时候永远是“自己等于自己”。
可以在shouldComponentUpdate钩子中加入一行验证自己的猜想。

	 console.log(nextProps.numberObject === this.props.numberObject);

在这里插入图片描述

相对应的,解决方案也有很多种。比如利用object.assign,深拷贝或者优秀的第三方js库等等。但是我在此文的最后依然还是要祭出官方提供的杀手锏。如果你仅仅只是为了在state和props不变化的情况下不触发render,可以直接拿出官方的pureComponent

pureComponent的基本用法


class Son extends React.PureComponent {render() {const { numberObject, index, handleClick } = this.props;//在每次渲染子组件时,打印该子组件的数字内容return <h1 onClick={() => handleClick(index)}>{numberObject.number}</h1>;}
}

http://www.ppmy.cn/news/46608.html

相关文章

8.Java面向对象---类、属性和方法

Java面向对象—类、属性和方法 在生活中&#xff0c;说到类&#xff0c;可以联想到类别&#xff0c;同类&#xff0c;会想到一类人&#xff0c;一类事物等等。而这一类人或事物都是具有相同特征或特点和行为的&#xff0c;我们根据不同的特征或特点和行为将他们归类或分类。同…

[golang gin框架] 24.Gin 商城项目-redis讲解以及操作

一.reids相关文章 Redis五种数据类型及其应用场景 REDIS中的缓存穿透&#xff0c;缓存击穿&#xff0c;缓存雪崩原因以及解决方案 redis实现用户签到&#xff0c;统计活跃用户&#xff0c;用户在线状态&#xff0c;用户留存率 [golang gin框架] 12.Gin 商城项目-base64Captcha生…

linux程序设置开机自动启动/etc/rc.d/rc.local, /etc/profile.d/

Linux 下设置开机启动的几种方法 1 修改 /etc/rc.d/rc.local 文件 把自启动脚本放在/etc/profile.d/下 为了保证一定能执行&#xff0c;最好再加个赋权操作 chmod x /etc/profile.d/test01.sh (test.sh为自己的脚本) 特点&#xff1a;在用MobaXterm等远程访问服务器的工具&a…

一个大二学生送给大一学弟学妹的建议

博主简介&#xff1a;先简单的介绍一下我吧&#xff0c;本人是一名大二学生&#xff0c;来自四川。目前所学专业是人工智能&#xff0c;致力于在CSDN平台分享自己的学习内容。 我为什么要写这篇文章&#xff1f; 我来到CSDN也已经一年了&#xff0c;在这一年里面&#xff0c;我…

【从零开始学Skynet】实战篇《球球大作战》(十):agent代码设计

现在开发登录流程涉及的最后一个服务agent&#xff0c;完成后就可以真正地把框架运行起来了。还会演示agent的单机功能&#xff0c;做个“打工”小游戏。 1、消息分发 玩家登录后&#xff0c;gateway会将客户端协议转发给agent&#xff08;流程图的阶段⑨&#xff09;。 新建se…

Hive表操作

插入数据sql、导出数据sql 1.insert 语法格式为&#xff1a; 基本的插入语法&#xff1a; INSERT OVERWRITE TABLE tablename [PARTITON(partcol1val1,partclo2val2)]select_statement FROM from_statementinsert overwrite table test_insert select * from test_…

4.34、组播(多播)

4.34、多播 1.组播(多播)的介绍①组播地址②如何设置组播&#xff08;组播的使用&#xff09; 2.代码编写①服务端②客户端 1.组播(多播)的介绍 单播地址标识单个 IP 接口&#xff0c;广播地址标识某个子网的所有 IP 接口&#xff0c;多播地址标识一组 IP 接口。单播和广播是寻…

Java每日一练(20230416)

目录 1. 三数之和 &#x1f31f;&#x1f31f; 2. 基本计算器 &#x1f31f;&#x1f31f;&#x1f31f; 3. 通配符匹配 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java…

ubuntu输入法问题汇总

Xfce4桌面环境输入法 Ubuntu20.04、ubuntu21.04中安装xfce4桌面环境&#xff0c;自带中文输入法&#xff1b; 原生xubuntu20.04中文输入法问题解决办法&#xff1a; 更新语言支持失败的话&#xff0c;终端键入&#xff1a;sudo apt-get install cmake qt5-default qtcreator…

在Github中77k星的王炸AutoGPT,会独立思考,直接释放双手

文章目录 1 前言1.1 什么是AutoGPT1.2 为什么是AutoGPT 2 AutoGPT部分实例2.1 类似一个Workflow2.2 市场调研2.3 自己写播客2.4 接入客服 3 安装和使用AutoGPT3.1 安装3.2 基础用法3.3 配置OpenAI的API3.4 配置谷歌API3.5 配置Pinecone API 4.讨论 1 前言 迄今为止&#xff0c…

Java基本类型和包装类型int和Integer

Java基本类型和包装类型int和Integer 基本类型和包装类型的区别使用中的问题 基本类型和包装类型的区别 Java中的数据类型可以分为两种&#xff1a;基本类型&#xff08;Primitive Type&#xff09;和包装类型&#xff08;Wrapper Class&#xff09;。这两者之间也有几个区别&…

【UE4】关卡流送的demo

关卡流送功能可以将地图文件加载到内存中&#xff0c;或者从内存中卸载&#xff0c;并在游戏过程中切换地图的可视性。 这样一来&#xff0c;场景便能拆分为较小的地图块&#xff0c;并且只有相关部分才会占用资源并被渲染。 正确设置后&#xff0c;开发者便能创建大型、无缝衔…

面试题:Ajax、Fetch、Axios三者的区别

Ajax 它的全称是&#xff1a;Asynchronous JavaScript And XML&#xff0c;翻译过来就是“异步的 Javascript 和 XML”。 Ajax 是一个技术统称&#xff0c;是一个概念模型&#xff0c;它囊括了很多技术&#xff0c;并不特指某一技术&#xff0c; Ajax 是一种思想&#xff0c;X…

java的泛型

1. 泛型是什么 ​ Java泛型是J2 SE1.5中引入的一个新特性&#xff0c;其本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数&#xff08;type parameter&#xff09;, 这种参数类型可以用在类、接口和方法的创建中&#xff0c;分别称为泛型类、泛型接口、…

Dell Inspiron 5570电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件型号驱动情况 主板Dell Inspiron 5570 处理器Intel(R) Core(TM) i7-8550U CPU 1.80GHz已驱动 内存8 GB 2400 MHz DDR4已驱动 硬盘samsung ssd 850 evo 250 go已驱…

数据结构——排序

排序 一、排序的概念二、直接插入排序希尔排序 三、直接选择排序四、堆排序1、堆的概念2、堆排序 五、冒泡排序六、快速排序七、归并排序八、基数排序排序算法的时间复杂度和空间复杂度 一、排序的概念 课本概念&#xff08;P165&#xff09; (1&#xff09;内部排序。内部排…

实现3D动画

一、transform Transform是形变的意思&#xff08;通常也叫变换&#xff09;&#xff0c;transformer就是变形金刚 常见的函数transform function有&#xff1a; 平移&#xff1a;translate(x, y) 缩放&#xff1a;scale(x, y) 旋转&#xff1a;rotate(deg) 倾斜&#xff1a;sk…

TensorFlow 和 Keras 应用开发入门:1~4 全

原文&#xff1a;Beginning Application Development with TensorFlow and Keras 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形…

移动端树形结构

该组件依据需求来做&#xff0c;当前包含三种选择状态&#xff0c;选中&#xff0c;未选中&#xff0c;半选。由于不需要做树形的收缩展开故没有写相关内容。树形展开与收缩与选中类似&#xff0c;只需要在节点上挂载相关字段即可实现。由于需求需要增加不限的功能&#xff0c;…

Java基础 泛型

问题1. B继承A 为什么 List<B> 不能赋值给List<A> 假设有如下代码 class A{} class B extends A{}List<B> b new ArrayList<>(); List<A> a b; // 编译报错List<A> List<B> 在运行时泛型被抹除&#xff0c; 都是List类型&#…