[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 当父组件被重新渲染的时候,也会触发子组件的重新渲染,这样就多出了无意义的性能开销。如果子组件的状态没有发生变化,则子组件是不需要被重新渲…...
云中网络的隔离GREVXLAN
底层的物理网络设备组成的网络我们称为 Underlay 网络,而用于虚拟机和云中的这些技术组成的网络称为 Overlay 网络,这是一种基于物理网络的虚拟化网络实现。 第一个技术是 GRE,全称 Generic Routing Encapsulation,它是一种 IP-o…...
【【萌新的RiscV学习之流水线控制-9】】
萌新的RiscV学习之流水线控制-9 我们按照在之前的单周期设计加入控制单元 那么我们能够在后续的设计中提供方便 我们也在流水线中加入一个control单元 我们先按照书上的指令op码值介绍一遍基本功能 接下来我们讲述control 的 控制效果 关于这些串口判别的使用 由于控制线从…...
MySQL 通过存储过程高效插入100w条数据
目录 一、前言二、创建表三、编写存储过程插入数据四、高效插入数据方案4.1、插入数据时删除表中全部索引4.2、存储过程中使用统一事务插入(性能显著提升)4.3、调整MySQL系统配置(性能显著提升,适合存储过程没有使用统一事务&…...
国庆10.1
用select实现服务器并发 ser #include <myhead.h> #define ERR_MSG(msg) do{\fprintf(stderr, "__%d__", __LINE__);\perror(msg);\ }while(0)#define PORT 8888 //端口号,范围1024~49151 #define IP "192.168.1.205" //本机…...
[C++_containers]10分钟让你掌握vector
前言 在一个容器的创建或是使用之前,我们应该先明白这个容器的一些特征。 我们可以通过文档来来了解,当然我也会将重要的部分写在下面。 1. vector 是表示可变大小数组的序列容器。 2. 就像数组一样, vector 也采用的连续存储空间来存储元…...
前端与后端:程序中两个不同的领域
前端和后端是构成一个完整的计算机应用系统的两个主要部分。它们分别负责不同的功能和任务,有以下几个方面的区别: 功能:前端主要负责用户界面的呈现和交互,包括网页的设计、布局、样式、动画效果和用户输入等。后端则处理网站或应…...
vue3 +elementplus | vue2+elementui 动态地通过验证规则子新增或删除单个表单字段
效果图 点击 ‘’ 新增一行,点击‘-’ 删除一行 vue3elementplus写法 template <el-dialog v-model"dialogFormVisible" :title"title"><el-form ref"ruleFormRef" :model"form" :inline"true" lab…...
STM32之DMA
简介 • DMA ( Direct Memory Access )直接存储器存取 (可以直接访问STM32内部存储器,如SRAM、程序存储器Flash和寄存器等) •DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预&a…...
解决前端二进制流下载的文件(例如:excel)打不开的问题
1. 现在后端请求数据后,返回了一个二进制的数据,我们要把它下载下来。 这是响应的数据: 2. 这是调用接口的地方: uploadOk(){if(this.files.length 0){return this.$Message.warning("请选择上传文件!ÿ…...
动态规划算法(1)--矩阵连乘和凸多边形剖分
目录 一、动态数组 1、创建动态数组 2、添加元素 3、删除修改元素 4、访问元素 5、返回数组长度 6、for each遍历数组 二、输入多个数字 1、正则表达式 2、has.next()方法 三、矩阵连乘 1、什么是矩阵连乘? 2、动态规划思路 3、手推m和s矩阵 4、完…...
通过Nginx重新认识HTTP错误码
文章目录 概要一、HTTP错误码1.1、1xx1.2、2xx1.3、3xx1.4、4xx1.5、5xx 二、Nginx对常见错误处理三、参考资料 概要 在web开发过程中,通过HTTP错误码快速定位问题是一个非常重要的技能,同时Nginx是非常常用的一个实现HTTP协议的服务,因此本…...
某房产网站登录RSA加密分析
文章目录 1. 写在前面2. 抓包分析3. 扣加密代码4. 还原加密 1. 写在前面 今天是国庆节,首先祝福看到这篇文章的每一个人节日快乐!假期会老的这些天一直在忙事情跟日常带娃,抽不出一点时间来写东西。夜深了、娃也睡了。最近湖南开始降温了&…...
深度学习:基于长短时记忆网络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后,保存到一个文件gtb_cookies.txt中。 然后用selenium使用这个cookies文件,免登录上github。但是报错如下:selenium.common.exceptions.UnableToSetCookieException: Message: unable to set co…...
初阶数据结构(四)带头双向链表
💓博主csdn个人主页:小小unicorn ⏩专栏分类:数据结构 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 带头双向链表 链表的相关介绍初始化链表销毁链…...
2022年9月及10月
9月 1.Halcon12的HObject和Hobject halcon12 可以用HObject,也可以用Hobject,用法都一样 包括HalconCpp.h 如果附加目录中: C:\Program Files\MVTec\HALCON-12.0\include\halconcpp\ 在前面,则用 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是一种非对称加密方式。发送者把明文通过公钥加密后发送出去,接受者把密文通过私钥解密得到明文。 算法过程 生成公钥和私钥 选取两个质数p和q,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 修…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
