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

news2024/11/29 4:02:10

文章目录

      • 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 得到延迟的 kw
  const 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) return
  const items = Array(40000)
    .fill(props.query)
    .map((item, i) => <p key={i}>{item}</p>)

  return items
}

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

优化2

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1055371.html

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

相关文章

计算机网络笔记 第二章 物理层

2.1 物理层概述 物理层要实现的功能 物理层接口特性 机械特性 形状和尺寸引脚数目和排列固定和锁定装置 电气特性 信号电压的范围阻抗匹配的情况传输速率距离限制 功能特性 -规定接口电缆的各条信号线的作用 过程特性 规定在信号线上传输比特流的一组操作过程&#xff0…

论文研读 - share work - QPipe:一种并行流水线的查询执行引擎

QPipe&#xff1a;一种并行流水线的查询执行引擎 QPipe: A Simultaneously Pipelined Relational Query Engine 关系型数据库通常独立执行并发的查询&#xff0c;每个查询都需执行一系列相关算子。为了充分利用并发查询中的数据扫描与计算&#xff0c;现有研究提出了丰富的技术…

进程之间的通信方式(共享存储,消息传递,管道通信)

进程通信 进程间通信&#xff08;Inter-Process Communication&#xff0c;IPC&#xff09;是指两个进程之间产生数据交互。进程是分配系统资源的单位(包括内存地址空间)&#xff0c;因此各进程拥有的内存地址空间相互独立。为了保证安全&#xff0c;一个进程不能直接访问另一…

计算机网络学习易错点

目录 概述 1.internet和Internet的区别 2.面向连接和无连接 3.不同的T 4.传输速率和传播速率 5.传播时延和传输时延&#xff08;发送时延&#xff09; 6.语法&#xff0c;语义和同步 一.物理层 1.传输媒体与物理层 2.同步通信和异步通信 3.位同步&#xff08;比特同…

【算法分析与设计】贪心算法(下)

目录 一、单源最短路径1.1 算法基本思想1.2 算法设计思想1.3 算法的正确性和计算复杂性1.4 归纳证明思路1.5 归纳步骤证明 二、最小生成树2.1 最小生成树性质2.1.1 生成树的性质2.1.2 生成树性质的应用 2.2 Prim算法2.2.1 正确性证明2.2.2 归纳基础2.2.3 归纳步骤2.3 Kruskal算…

debian设置允许ssh连接

解决新debian系统安装后不能通过ssh连接的问题。 默认情况下&#xff0c;Debian系统不开启SSH远程登录&#xff0c;需要手动安装SSH软件包并设置开机启动。 > 设置允许root登录传送门&#xff1a;debian设置允许root登录 首先检查/etc/ssh/sshd_config文件是否存在。 注意…

TFT LCD刷新原理及LCD时序参数总结(LCD时序,写的挺好)

cd工作原理目前不了解&#xff0c;日后会在博客中添加这一部分的内容。 1.LCD工作原理[1] 我对LCD的工作原理也仅仅处在了解的地步&#xff0c;下面基于NXP公司对LCD工作原理介绍的ppt来学习一下。 LCD(liquid crystal display,液晶显示屏) 是由液晶段阵列组成&#xff0c;当…

【EasyPoi】SpringBoot使用EasyPoi自定义模版导出Excel

EasyPoi 官方文档&#xff1a;http://doc.wupaas.com/docs/easypoi Excel模版导出 引入依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency…

newstarctf

wp web: 1.rce 可以发现这个变量名有下划线也有点。 $code$_POST[e_v.a.l]; 这时候如果直接按这个变量名来传参&#xff0c;php 是无法接收到这个值的&#xff0c;具体原因是 php 会自动把一些不合法的字符转化为下划线&#xff08;注&#xff1a;php8以下&#xff09;&am…

【论文极速读】IMAGEBIND —— 通过图片作为桥梁桥联多模态语义

【论文极速读】IMAGEBIND —— 通过图片作为桥梁桥联多模态语义 FesianXu 20230929 at Baidu Search Team 前言 当前大部分多模态工作都集中在图片-文本、视频-文本中&#xff0c;关于音频、深度图、热值图的工作则比较少&#xff0c;在IMAGEBIND中&#xff0c;作者提出了一种…

Neo4j最新安装教程(图文版)

目录 一、软件介绍 二、下载软件 1、官方下载 2、云盘下载 三、安装教程 1、首先配置Neo4j的环境变量 2、启动neo4j服务器 3、访问界面 一、软件介绍 官网地址&#xff1a;https://neo4j.com/ Neo4j是一个高性能、可扩展的图数据库管理系统。它专注于存储、查询和处理大…

Explain执行计划字段解释说明---ID字段说明

ID字段说明 1、select查询的序列号,包含一组数字&#xff0c;表示查询中执行select子句或操作表的顺序 2、ID的三种情况 &#xff08;1&#xff09;id相同&#xff0c;执行顺序由上至下。 &#xff08;2&#xff09;id不同&#xff0c;如果是子查询&#xff0c;id的序号会…

大数据Flink(九十四):DML:TopN 子句

文章目录 DML:TopN 子句 DML:TopN 子句 TopN 定义(支持 Batch\Streaming):TopN 其实就是对应到离线数仓中的 row_number(),可以使用 row_number() 对某一个分组的数据进行排序 应用场景

cesium 雷达扫描 (扫描线)

cesium 雷达扫描 (扫描线) 1、实现方法 图中的线使用polyline方法绘制,外面的圆圈是用ellipse方法绘制(当然也不指这一种方法),图中线的扫描转动效果是实时改变线的经纬度来实现(知道中心点经纬度、又已知方向和距离可以求出端点的经纬度)使用CallbackProperty方法来实…

数据结构与算法——18.avl树

这篇文章我们来看一下avl树 目录 1.概述 2.AVL树的实现 1.概述 我们前面讲了二叉搜索树&#xff0c;它是有一个key值&#xff0c;然后比父节点key值大的在左边&#xff0c;小的在右边。这样设计是为了便于查找。但是有一种极端的情况&#xff0c;就是所有的结点都在一边&am…

7.3 调用函数

前言&#xff1a; 思维导图&#xff1a; 7.3.1 函数调用的形式 我的笔记&#xff1a; 函数调用的形式 在C语言中&#xff0c;调用函数是一种常见的操作&#xff0c;主要有以下几种调用方式&#xff1a; 1. 函数调用语句 此时&#xff0c;函数调用独立存在&#xff0c;作为…

艺术表现形式

abstract expressionism 抽象表现主义 20世纪中期的一种艺术运动&#xff0c;包括多种风格和技巧&#xff0c;特别强调艺术家通过非传统和通常非具象的手段表达态度和情感的自由。 抽象表现主义用有力的笔触和滴落的颜料来表达情感和自发性。 简单地结合“abstract expression…

基于Java的服装销售平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

paddle2.3-基于联邦学习实现FedAVg算法-CNN

目录 1. 联邦学习介绍 2. 实验流程 3. 数据加载 4. 模型构建 5. 数据采样函数 6. 模型训练 1. 联邦学习介绍 联邦学习是一种分布式机器学习方法&#xff0c;中心节点为server&#xff08;服务器&#xff09;&#xff0c;各分支节点为本地的client&#xff08;设备&#…

【k8s】集群搭建篇

文章目录 搭建kubernetes集群kubeadm初始化操作安装软件(master、所有node节点)Kubernetes Master初始化Kubernetes Node加入集群部署 CNI 网络插件测试 kubernetes 集群停止服务并删除原来的配置 二进制搭建(单master集群)初始化操作部署etcd集群安装Docker部署master节点解压…