当前位置: 首页 > news >正文

[React] 性能优化相关 (一)

文章目录

      • 1.React.memo
      • 2.useMemo
      • 3.useCallback
      • 4.useTransition
      • 5.useDeferredValue

1.React.memo

当父组件被重新渲染的时候,也会触发子组件的重新渲染,这样就多出了无意义的性能开销。如果子组件的状态没有发生变化,则子组件是不需要被重新渲染的。

const 组件 = React.memo(函数式组件)

父组件声明了 count 和 flag 两个状态,子组件依赖于父组件通过 props 传递的 num。当父组件修改 flag 的值时,会导致子组件的重新渲染。

import React, { useEffect, useState } from 'react'// 父组件
export const Father: React.FC = () => {// 定义 count 和 flag 两个状态const [count, setCount] = useState(0)const [flag, setFlag] = useState(false)return (<><h1>父组件</h1><p>count 的值是:{count}</p><p>flag 的值是:{String(flag)}</p><button onClick={() => setCount((prev) => prev + 1)}>+1</button><button onClick={() => setFlag((prev) => !prev)}>Toggle</button><hr /><Son num={count} /></>)
}// 子组件:依赖于父组件通过 props 传递进来的 num
export const Son: React.FC<{ num: number }> = ({ num }) => {useEffect(() => {console.log('触发了子组件的渲染')})return (<><h3>子组件 {num}</h3></>)
}

React.memo(函数式组件) 将子组件包裹起来, 只有子组件依赖的 props 发生变化的时候, 才会触发子组件的重新渲染。

// 子组件:依赖于父组件通过 props 传递进来的 num
export const Son: React.FC<{ num: number }> = React.memo(({ num }) => {useEffect(() => {console.log('触发了子组件的渲染')})return (<><h3>子组件 --- {num}</h3></>)
})

2.useMemo

在 Father 组件中添加一个“计算属性”, 根据 flag 值的真假, 返回不同的内容。

// 父组件
export const Father: React.FC = () => {// 定义 count 和 flag 两个状态const [count, setCount] = useState(0)const [flag, setFlag] = useState(false)// 根据布尔值进行计算,动态返回内容const tips = () => {console.log('触发了 tips 的重新计算')return flag ? <p>1</p> : <p>2</p>}return (<><h1>父组件</h1><p>count 的值是:{count}</p><p>flag 的值是:{String(flag)}</p>{tips()}<button onClick={() => setCount((prev) => prev + 1)}>+1</button><button onClick={() => setFlag((prev) => !prev)}>Toggle</button><hr /><Son num={count} /></>)
}

点击父组件中的 +1 按钮, 发现 count 在自增, flag 不会改变, 此时也会触发 tips 函数的重新执行, 造成性能的浪费。
希望如果 flag 没有发生变化, 则避免 tips 函数的重新计算, 从而优化性能。此时需要用到 React Hooks 提供的 useMemo API。

useMemo()

const memoValue = useMemo(() => {return 计算得到的值
}, [value]) // 表示监听 value 的变化
  • 第一个参数为函数, 用于处理计算的逻辑, 必须用到得到的值。
  • 第二个参数为数组, 为依赖项, 依赖项发生变化, 触发之前的第一个参数的函数的执行。如果不传的话, 每次更新都会重新计算。如果传入的是[], 那么只会第一个render的时候触发, 以后不会重新渲染。
import React, { useEffect, useState, useMemo } from 'react'// 根据布尔值进行计算,动态返回内容
const tips = useMemo(() => {console.log('触发了 tips 的重新计算')return flag ? 1 : 2
}, [flag])

此时点击+1, 不会触发tips的重新计算。

3.useCallback

useMemo 能够达到缓存某个变量值的效果, 而当前要学习的 useCallback 用来对组件内的函数进行缓存, 返回的是函数。

useCallback 会返回一个 memorized 回调函数供组件使用, 从而防止组件每次 rerender 时反复创建相同的函数, 节省内存, 提高性能。

  • 第一个参数为一个函数, 处理业务逻辑, 这个函数就是需要被缓存的函数

  • 第二个参数为依赖项列表, 依赖项变化时才会重新执行 useCallback。如果省略的话, 每次更新都会重新计算, 如果为[], 就只是第一次render的时候触发一次。

当输入框触发 onChange 事件时, 会给 kw 重新赋值。kw 值的改变会导致组件的 rerender, 组件的 rerender 会导致反复创建 onKwChange 函数并添加到 Set 集合, 造成了不必要的内存浪费。

import React, {useState} from 'react';// 用来存储函数的 set 集合
const set = new Set()const Search:React.FC = () => {const [kw, setKw] = useState('')const onKwChange = (e: React.ChangeEvent<HTMLInputElement>) => {setKw(e.currentTarget.value)}// 把 onKwChange 函数的引用,存储到 set 集合中set.add(onKwChange)// 打印 set 集合中元素的数量console.log('set 中函数的数量为:' + set.size)return (<><input type="text" value={kw} onChange={onKwChange} /><hr /><p>{kw}</p><p></p></>)
}export default Search;

每次文本框的值发生变化, 会打印set.size的值, 这个值一直在自增 +1, 因为每次组件 rerender 都会创建一个新的 onKwChange 函数添加到 set 集合中。

为了防止 Search 组件 rerender 时每次都会重新创建 onKwChange 函数, 可以使用 useCallback 对这个函数进行缓存。

import React, {useCallback, useState} from 'react';// 用来存储函数的 set 集合
const set = new Set()const Search:React.FC = () => {const [kw, setKw] = useState('')const onKwChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {setKw(e.currentTarget.value)}, [])// 把 onKwChange 函数的引用,存储到 set 集合中set.add(onKwChange)// 打印 set 集合中元素的数量console.log('set 中函数的数量为:' + set.size)return (<><input type="text" value={kw} onChange={onKwChange} /><hr /><p>{kw}</p><p></p></>)
}export default Search;

无论 input 的值如何发生变化, 每次打印的 set.size 的值都是 1。证明我们使用 useCallback 实现了对函数的缓存。

4.useTransition

useTransition 可以将一个更新转为低优先级更新, 使其可以被打断, 不阻塞 UI 对用户操作的响应, 能够提高用户的使用体验。它常用于优化视图切换时的用户体验。

Home、Movie、About 3个标签, 其中Movie 是一个很耗性能的组件, 在渲染 Movie 组件期间页面的 UI 会被阻塞, 用户会感觉页面十分卡顿。

import React, { useState } from 'react'export const TabsContainer: React.FC = () => {// 被激活的标签页的名字const [activeTab, setActiveTab] = useState('home')// 点击按钮,切换激活的标签页const onClickHandler = (tabName: string) => {setActiveTab(tabName)}return (<div style={{ height: 500 }}><TabButton isActive={activeTab === 'home'} onClick={() => onClickHandler('home')}>首页</TabButton><TabButton isActive={activeTab === 'movie'} onClick={() => onClickHandler('movie')}>电影</TabButton><TabButton isActive={activeTab === 'about'} onClick={() => onClickHandler('about')}>关于</TabButton><hr />{/* 根据被激活的标签名,渲染对应的 tab 组件 */}{activeTab === 'home' && <HomeTab />}{activeTab === 'movie' && <MovieTab />}{activeTab === 'about' && <AboutTab />}</div>)
}// Button 组件 props 的 TS 类型
type TabButtonType = React.PropsWithChildren & { isActive: boolean; onClick: () => void }
// Button 组件
const TabButton: React.FC<TabButtonType> = (props) => {const onButtonClick = () => {props.onClick()}return (<button className={['btn', props.isActive && 'active'].join(' ')} onClick={onButtonClick}>{props.children}</button>)
}// Home 组件
const HomeTab: React.FC = () => {return <>HomeTab</>
}// Movie 组件
const MovieTab: React.FC = () => {const items = Array(100000).fill('MovieTab').map((item, i) => <p key={i}>{item}</p>)return items
}// About 组件
const AboutTab: React.FC = () => {return <>AboutTab</>
}
.btn {margin: 5px;background-color: rgb(8, 92, 238);color: #fff;transition: opacity 0.5s ease;
}.btn:hover {opacity: 0.6;transition: opacity 0.5s ease;
}.btn.active {background-color: rgb(3, 150, 0);
}

语法结构

import { useTransition } from 'react';function TabContainer() {const [isPending, startTransition] = useTransition();// ……
}
  • isPending 布尔值: 是否存在待处理的 transition, 如果值为 true, 说明页面上存在待渲染的部分, 可以给用户一个加载的提示。
  • startTransition 函数: 调用此函数, 可以将状态的更新标记为低优先级的, 不阻塞UI 对用户操作的响应。

使用 useTransition 把点击按钮后为 activeTab 赋值的操作, 标记为低优先级, UI 不被阻塞。

import React, { useState, useTransition } from 'react'export const TabsContainer: React.FC = () => {// 被激活的标签页的名字const [activeTab, setActiveTab] = useState('home')const [, startTransition] = useTransition()// 点击按钮,切换激活的标签页const onClickHandler = (tabName: string) => {startTransition(() => {setActiveTab(tabName)})}
}

点击 Movie 按钮后, 状态的更新被标记为低优先级, About 按钮的 hover 效果和点击操作都会被立即响应。

5.useDeferredValue

useDeferredValue 提供一个 state 的延迟版本, 根据其返回的延迟的 state 能够推迟更新 UI 中的某一部分。

import { useState, useDeferredValue } from 'react';function SearchPage() {const [kw, setKw] = useState('');// 根据 kw 得到延迟的 kwconst deferredKw = useDeferredValue(kw);// ...
}

SearchResult 组件会根据用户输入的关键字, 循环生成大量的p标签, 会浪费性能。

import React, { useState } from 'react'// 父组件
export const SearchBox: React.FC = () => {const [kw, setKw] = useState('')const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {setKw(e.currentTarget.value)}return (<div style={{ height: 500 }}><input type="text" value={kw} onChange={onInputChange} /><hr /><SearchResult query={kw} /></div>)
}// 子组件,渲染列表项
const SearchResult: React.FC<{ query: string }> = (props) => {if (!props.query) returnconst items = Array(40000).fill(props.query).map((item, i) => <p key={i}>{item}</p>)return items
}

优化1
在这里插入图片描述

优化2

当 kw 的值频繁更新时, deferredKw 的值会明显滞后, 此时用户在页面上看到的列表数据并不是最新的, 给内容添加 opacity 透明度, 表明当前看到的内容已过时。

在这里插入图片描述

相关文章:

[React] 性能优化相关 (一)

文章目录 1.React.memo2.useMemo3.useCallback4.useTransition5.useDeferredValue 1.React.memo 当父组件被重新渲染的时候&#xff0c;也会触发子组件的重新渲染&#xff0c;这样就多出了无意义的性能开销。如果子组件的状态没有发生变化&#xff0c;则子组件是不需要被重新渲…...

云中网络的隔离GREVXLAN

底层的物理网络设备组成的网络我们称为 Underlay 网络&#xff0c;而用于虚拟机和云中的这些技术组成的网络称为 Overlay 网络&#xff0c;这是一种基于物理网络的虚拟化网络实现。 第一个技术是 GRE&#xff0c;全称 Generic Routing Encapsulation&#xff0c;它是一种 IP-o…...

【【萌新的RiscV学习之流水线控制-9】】

萌新的RiscV学习之流水线控制-9 我们按照在之前的单周期设计加入控制单元 那么我们能够在后续的设计中提供方便 我们也在流水线中加入一个control单元 我们先按照书上的指令op码值介绍一遍基本功能 接下来我们讲述control 的 控制效果 关于这些串口判别的使用 由于控制线从…...

MySQL 通过存储过程高效插入100w条数据

目录 一、前言二、创建表三、编写存储过程插入数据四、高效插入数据方案4.1、插入数据时删除表中全部索引4.2、存储过程中使用统一事务插入&#xff08;性能显著提升&#xff09;4.3、调整MySQL系统配置&#xff08;性能显著提升&#xff0c;适合存储过程没有使用统一事务&…...

国庆10.1

用select实现服务器并发 ser #include <myhead.h> #define ERR_MSG(msg) do{\fprintf(stderr, "__%d__", __LINE__);\perror(msg);\ }while(0)#define PORT 8888 //端口号&#xff0c;范围1024~49151 #define IP "192.168.1.205" //本机…...

[C++_containers]10分钟让你掌握vector

前言 在一个容器的创建或是使用之前&#xff0c;我们应该先明白这个容器的一些特征。 我们可以通过文档来来了解&#xff0c;当然我也会将重要的部分写在下面。 1. vector 是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c; vector 也采用的连续存储空间来存储元…...

前端与后端:程序中两个不同的领域

前端和后端是构成一个完整的计算机应用系统的两个主要部分。它们分别负责不同的功能和任务&#xff0c;有以下几个方面的区别&#xff1a; 功能&#xff1a;前端主要负责用户界面的呈现和交互&#xff0c;包括网页的设计、布局、样式、动画效果和用户输入等。后端则处理网站或应…...

vue3 +elementplus | vue2+elementui 动态地通过验证规则子新增或删除单个表单字段

效果图 点击 ‘’ 新增一行&#xff0c;点击‘-’ 删除一行 vue3elementplus写法 template <el-dialog v-model"dialogFormVisible" :title"title"><el-form ref"ruleFormRef" :model"form" :inline"true" lab…...

STM32之DMA

简介 • DMA &#xff08; Direct Memory Access &#xff09;直接存储器存取 &#xff08;可以直接访问STM32内部存储器&#xff0c;如SRAM、程序存储器Flash和寄存器等&#xff09; •DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&a…...

解决前端二进制流下载的文件(例如:excel)打不开的问题

1. 现在后端请求数据后&#xff0c;返回了一个二进制的数据&#xff0c;我们要把它下载下来。 这是响应的数据&#xff1a; 2. 这是调用接口的地方&#xff1a; uploadOk(){if(this.files.length 0){return this.$Message.warning("请选择上传文件&#xff01;&#xff…...

动态规划算法(1)--矩阵连乘和凸多边形剖分

目录 一、动态数组 1、创建动态数组 2、添加元素 3、删除修改元素 4、访问元素 5、返回数组长度 6、for each遍历数组 二、输入多个数字 1、正则表达式 2、has.next()方法 三、矩阵连乘 1、什么是矩阵连乘&#xff1f; 2、动态规划思路 3、手推m和s矩阵 4、完…...

通过Nginx重新认识HTTP错误码

文章目录 概要一、HTTP错误码1.1、1xx1.2、2xx1.3、3xx1.4、4xx1.5、5xx 二、Nginx对常见错误处理三、参考资料 概要 在web开发过程中&#xff0c;通过HTTP错误码快速定位问题是一个非常重要的技能&#xff0c;同时Nginx是非常常用的一个实现HTTP协议的服务&#xff0c;因此本…...

某房产网站登录RSA加密分析

文章目录 1. 写在前面2. 抓包分析3. 扣加密代码4. 还原加密 1. 写在前面 今天是国庆节&#xff0c;首先祝福看到这篇文章的每一个人节日快乐&#xff01;假期会老的这些天一直在忙事情跟日常带娃&#xff0c;抽不出一点时间来写东西。夜深了、娃也睡了。最近湖南开始降温了&…...

深度学习:基于长短时记忆网络LSTM实现情感分析

目录 1 LSTM网络介绍 1.1 LSTM概述 1.2 LSTM网络结构 1.3 LSTM门机制 1.4 双向LSTM 2 Pytorch LSTM输入输出 2.1 LSTM参数 2.2 LSTM输入 2.3 LSTM输出 2.4 隐藏层状态初始化 3 基于LSTM实现情感分析 3.1 情感分析介绍 3.2 数据集介绍 3.3 基于pytorch的代码实现 3…...

selenium使用已经获取的cookies登录网站报错unable to set cookie的处理方式

用selenium半手动登录github获取其登录cookies后&#xff0c;保存到一个文件gtb_cookies.txt中。 然后用selenium使用这个cookies文件&#xff0c;免登录上github。但是报错如下&#xff1a;selenium.common.exceptions.UnableToSetCookieException: Message: unable to set co…...

初阶数据结构(四)带头双向链表

&#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;数据结构 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &#x1f339;&#x1f339;&#x1f339;关注我带你学习编程知识 带头双向链表 链表的相关介绍初始化链表销毁链…...

2022年9月及10月

9月 1.Halcon12的HObject和Hobject halcon12 可以用HObject&#xff0c;也可以用Hobject&#xff0c;用法都一样 包括HalconCpp.h 如果附加目录中&#xff1a; C:\Program Files\MVTec\HALCON-12.0\include\halconcpp\ 在前面&#xff0c;则用 HalconCpp::HObject 如果附加目录…...

Vmware安装

title: “Vmware安装” createTime: 2021-11-22T09:53:2908:00 updateTime: 2021-11-22T09:53:2908:00 draft: false author: “name” tags: [“VMware”,“安装”,“linux”] categories: [“install”] description: “测试的” linux安装VMware Workstation16 1.安装包 …...

RSA算法

算法简介 RSA是一种非对称加密方式。发送者把明文通过公钥加密后发送出去&#xff0c;接受者把密文通过私钥解密得到明文。 算法过程 生成公钥和私钥 选取两个质数p和q&#xff0c;np*q。n的长度就是密钥长度。φ(n)(p-1)*(q-1)φ(n)为n的欧拉函数。找到1-φ(n)间与φ(n)互质的…...

计算机竞赛 深度学习手势识别 - yolo python opencv cnn 机器视觉

文章目录 0 前言1 课题背景2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存 5 模型训练5.1 修…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

GraphQL 实战篇:Apollo Client 配置与缓存

GraphQL 实战篇&#xff1a;Apollo Client 配置与缓存 上一篇&#xff1a;GraphQL 入门篇&#xff1a;基础查询语法 依旧和上一篇的笔记一样&#xff0c;主实操&#xff0c;没啥过多的细节讲解&#xff0c;代码具体在&#xff1a; https://github.com/GoldenaArcher/graphql…...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

Qt的学习(二)

1. 创建Hello Word 两种方式&#xff0c;实现helloworld&#xff1a; 1.通过图形化的方式&#xff0c;在界面上创建出一个控件&#xff0c;显示helloworld 2.通过纯代码的方式&#xff0c;通过编写代码&#xff0c;在界面上创建控件&#xff0c; 显示hello world&#xff1b; …...

CMS内容管理系统的设计与实现:多站点模式的实现

在一套内容管理系统中&#xff0c;其实有很多站点&#xff0c;比如企业门户网站&#xff0c;产品手册&#xff0c;知识帮助手册等&#xff0c;因此会需要多个站点&#xff0c;甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...

智警杯备赛--excel模块

数据透视与图表制作 创建步骤 创建 1.在Excel的插入或者数据标签页下找到数据透视表的按钮 2.将数据放进“请选择单元格区域“中&#xff0c;点击确定 这是最终结果&#xff0c;但是由于环境启不了&#xff0c;这里用的是自己的excel&#xff0c;真实的环境中的excel根据实训…...