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

news2024/12/28 18:39:45

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 (
            <Son
              key={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 (
            <Son
              key={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.coloradmin.cn/o/424602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

北京小厂Java实习面经

目录1.数据库的三大范式2.事务四个特性3.知道多少种索引&#xff0c;分别讲讲4.主键索引和唯一索引的区别5.索引失效的场景6.数据库的日志知道哪些&#xff0c;分别讲讲7.redis的数据结构和应用场景8.缓存击穿是怎么产生的&#xff0c;解决方案9.redis中key的过期策略10.redis内…

达梦数据库支持GB18030-2022中文编码字符集证明方法

既然是证明就要做到有图有真相。 证明途径1&#xff1a;系统概览法 查看达梦数据库“管理工具”->“对象导航”->“管理服务器”-“系统概览”->“字符集编码”->”GB18030”. 看见”GB18030”字样就是证据之一。如果你没有看见这个字样&#xff0c;而是UTF8等其…

1. 大端法和小端法

int32_t num 0x01020304;一个int32_t是4个字节&#xff0c;在内存中的存储是高位字节在低地址&#xff0c;低位字节在高地址。 &#xff08;数字&#xff09;前者的高低是数字位数的高低&#xff0c;左边是高位数&#xff0c;右边是低位数&#xff1b; &#xff08;地址&…

git代码使用空格缩进

1、idea设置缩进符为空格 Java 代码 golang 代码 2、设置提交仓库时的空格处理&#xff0c;否则 golang 代码为了减少文件大小&#xff0c;可能会把空格缩进改为制表符。 设置当前仓库配置 git config core.whitespace "-trailing-space,space-before-tab" 设置当前…

Pytorch深度学习实战3-7:详解数据加载DataLoader与模型处理

目录1 数据集Dataset2 数据加载DataLoader3 常用预处理方法4 模型处理5 实例&#xff1a;MNIST数据集处理1 数据集Dataset Dataset类是Pytorch中图像数据集操作的核心类&#xff0c;Pytorch中所有数据集加载类都继承自Dataset父类。当我们自定义数据集处理时&#xff0c;必须实…

从TOP25榜单,看半导体之变

据SIA报告显示&#xff0c;2022年全球半导体销售额创历史新高达到5740亿美元。尽管2022年下半年&#xff0c;半导体市场出现了周期性的低迷&#xff0c;但其全年的销售额相较2021年增长了3.3%。 近日&#xff0c;市调机构Gartner发布了全球以及中国大陆TOP25名半导体厂商的排名…

js数组API的时间复杂度大全

一句话总结&#xff1a; 数组为连续且有序的数据结构, 所以若根据下标查找则很快&#xff0c;index[i]一步到位就可实现查询&#xff0c;若遍历查找则很慢(相对而言)。而插入和删除&#xff0c;除了数组末尾的增删很快&#xff0c;其它处则很慢&#xff0c;因为若数组某处要插入…

【服务器数据恢复】 重装系统导致xfs文件系统分区丢失的数据恢复案例

服务器数据恢复环境&#xff1a; EMC某型号存储&#xff0c;20块磁盘组建raid5磁盘阵列&#xff0c;划分2个lun。 服务器故障&#xff1a; 管理员执行重装系统操作后发现分区发生改变&#xff0c;原先的sdc3分区丢失&#xff0c;该分区采用xfs文件系统&#xff0c;存储了公司重…

开放式耳机好用吗,推荐几款不错的开放式耳机

​开放式耳机是一种新型的耳机&#xff0c;相比于传统的耳机&#xff0c;开放式耳机听歌时不需要将耳朵堵上&#xff0c;不会因为长时间佩戴而对听力造成损害。它不需要入耳也能听到声音&#xff0c;在户外运动时能够及时听到环境音&#xff0c;避免安全隐患。现在在骨传导市面…

nodejs+vue 沃健身房管理系统

3)系统分析 本章主要是对系统可行性、系统性能、还有系统功能需求进行分析。 (4)系统设计 对系统系统功能和数据库等进行详细讲解。 (5)系统的实现 主要对个人中心、课程分类管理、用户管理、健身器材管理、健身教练管理、预约教练管理、健身课程管理、课程订单管理、健身视频管…

ESP32学习笔记08-adc单通道数据采集

8. adc单通道数据采集 8.1RTC SAR ADC 控制器 8.2ADC相关的api 8.2.1 配置adc的位宽 esp_err_t adc1_config_width(adc_bits_width_t width_bit);width_bit :位宽 返回值 ESP_OK 配置成功 ESP_ERR_INVALID_ARG 参数错误 esp32最大的宽度的12位typedef enum {

STM8S208MB -> 寄存器方式实现对Flash的连续读写操作(IAR)

代码 File: STM8S208MB_Flash_Op.c /*file: STM8S208MB_Flash_Op.cbrief: 读写Flashdata: 2023-04-14author: ArcherQAQ */#include "STM8S208MB_Flash_Op.h" #include "stdio.h"u8 dataBuf[] {0xFF, 0xFF}; // 写入Flash的数据 u8 Rec_Buf[100] {0x00…

天猫数据分析:2023年速食品(方便面)市场数据分析

我国的方便面市场是一个比较活跃的市场&#xff0c;其市场规模也比较庞大。近年来&#xff0c;随着中国经济的发展&#xff0c;消费者对方便面的需求量和要求也在不断变化&#xff0c;因此&#xff0c;我国方便面市场的规模和消费者的需求环境也正在不断改变。 根据鲸参谋电商数…

Excel技能之排名,小函数很强大

你还在熬夜加班搞Excel吗&#xff1f; 你还在用手指&#xff0c;指着电脑屏幕&#xff0c;一行一行核对数据吗&#xff1f; 你还在害怕被笑而不敢问同事吗&#xff1f; 赶紧来学Excel&#xff0c;收藏加关注&#xff0c;偷偷地进步&#xff01;日积月累&#xff0c;必成大器&am…

12-python内存地址

1.查看内存地址 a1 print(id(a)) # 24319294835042.数据类型 &#xff08;1&#xff09;不可变数据类型&#xff1a;数值、字符串、布尔值、元组 数据存储在计算机中的某个位置&#xff0c;不管赋值给谁&#xff0c;内存地址都相同 a"jack" b"jack" prin…

常见分布式锁3:Redis setNx

Redis实现分布式锁的核心便在于SETNX命令&#xff0c;它是SET if Not eXists的缩写&#xff0c;如果键不存在&#xff0c;则将键设置为给定值&#xff0c;在这种情况下&#xff0c;它等于SET&#xff1b;当键已存在时&#xff0c;不执行任何操作&#xff1b;成功时返回1&#x…

【python游戏】努力制造阳光,让植物有力量对抗僵尸吧~

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 晃着脑袋生产阳光的向日葵&#xff0c;突突突吐着子弹的豌豆射手&#xff01;​ 行动迟缓种类丰富的僵尸…… 印象最深的是“僵尸吃掉了你的脑子&#xff01;” 还有疯狂的戴夫&#xff0c;无一不唤醒着我们的童年记忆​ 山…

Hive 拉链表的两种实现方式

目录 1.什么是拉链表 2.拉链表的产生背景 2.1数据同步 2.1.1全量同步 2.1.2增量同步 2.2增量同步和拉链表 3.拉链表的实现方式 3.1数据准备 3.2思路1 3.3思路2 1.什么是拉链表 我们首先要知道&#xff0c;拉链表是一个逻辑上的概念。 拉链表记录的是增量数据&#x…

(链表专题) 328. 奇偶链表 ——【Leetcode每日一题】

328. 奇偶链表 给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表。 第一个 节点的索引被认为是 奇数 &#xff0c; 第二个 节点的索引为 偶数 &#xff0c;以此类推。 请注意&#xff0c;偶…

在 RISC-V Linux 内核中添加模块

在 RISC-V Linux 内核中添加模块 flyfish 本例以添加helloworld字符设备为例 一 源码配置 1 源码 源码文件helloworld.c拷贝到 drivers/char 目录中 源码主要是输出Hello world init 2 Kconfig 打开drivers/char 目录下的Kconfig文件 在endmenu之前加上 config HELLO…