React性能优化

三个可以优化的地方

避免过度多次渲染 

组件会在以下情况下重新渲染

注意:例如组件组合的形式,<Test><Counter></Counter></Test>,即使Test发生了重新渲染,Counter也不会重新渲染。另外使用React这样的库或框架时,渲染指的是组件函数被调用的过程。这个过程并不一定意味着文档对象模型(DOM)实际上被更新了。它只是意味着组件的逻辑被执行,可能会重新计算组件的状态和属性。

 什么是过度渲染呢?

  • 无效渲染是指组件函数被调用了,但是并没有导致DOM的任何变化。
  • 这通常发生在组件的状态或属性没有改变,但组件仍然被重新渲染的情况下。这种渲染是不必要的,因为它消耗了资源但没有带来任何视觉上的变化。
  • 当然这在react中大部分情况下是没问题的,只有这种情况过度频繁时才会产生问题。

记忆化

组件、对象、函数都可以记忆化。

 memo
工作原理
  1. 创建不会重新渲染的组件

    • memo用于创建一个组件,当其父组件重新渲染时,如果传入的props在两次渲染之间没有变化,那么这个被memo包裹的组件将不会重新渲染。
  2. 只影响props

    • memo只对组件的props进行比较。如果一个被memo包裹的组件在props没有变化的情况下,即使它自己的state发生了变化,或者它订阅的context发生了变化,它仍然会重新渲染。

局限性

  1. 在React中,每次渲染都会重新创建所有内容(包括对象和函数): 这意味着,即使组件的状态没有改变,每次渲染时,组件内部的函数和对象都会被重新实例化。

  2. 在JavaScript中,两个看起来相同的对象或函数实际上是不同的({} != {}): 在JavaScript中,对象和函数是引用类型。即使两个对象的内容完全相同,它们在内存中的地址也是不同的。这意味着,即使两个对象看起来一样,它们也是两个不同的实例。

  3. 因此,如果将对象或函数作为props传递,子组件在每次重新渲染时都会将它们视为新的props: 当父组件将对象或函数作为props传递给子组件时,由于每次渲染都会创建新的实例,子组件会认为这些props是新的,即使它们的值没有变化。

  4. 如果props在重新渲染之间不同,memo将不起作用memo是React的一个优化技巧,它通过比较props来决定是否重新渲染组件。如果props在两次渲染之间没有变化,memo可以防止不必要的渲染。但是,如果props是对象或函数,即使它们的值没有变化,由于它们是新创建的实例,memo也会认为props已经改变,从而导致子组件重新渲染。

所以,我们需要对对象和函数进行memoization(记忆化),以使它们在重新渲染之间保持稳定(保持不变)

useMemo与useCallback
工作原理
  1. 用于记忆化(memoize)对象和函数

  2. 缓存机制

    • 当你将对象或函数传递给 useMemo 或 useCallback 时,这些值或函数会被存储在内存中(即被“缓存”)。在随后的重新渲染中,只要依赖项(即“输入”)没有变化,就会返回缓存的值或函数。
  3. 依赖数组

    • useMemo 和 useCallback 都接受一个依赖数组,这与 useEffect 的工作方式类似。依赖数组用于追踪哪些值或状态影响到了记忆化的值或函数。
    • 如果依赖数组中的任何一个值发生变化,那么记忆化的值或函数就会被重新创建。这是通过比较新旧依赖项来实现的,只有当依赖项发生变化时,才会触发重新计算。

 使用场景
  1. 防止不必要的渲染

    • 通过与 React.memo 一起使用,可以记忆化 props,以避免不必要的组件重新渲染。
  2. 避免每次渲染时的昂贵重新计算

    • 使用 useMemo 来记忆化那些计算成本较高的值。这样,只有在其依赖项发生变化时,才会重新计算这些值,从而节省性能。
  3. 记忆化在其他 Hook 的依赖数组中使用的值

    • 当某个值被用作另一个 Hook(如 useEffect)的依赖数组中的项时,使用 useMemo 或 useCallback 来记忆化这个值可以避免无限循环的副作用。例如,如果你在 useEffect 的依赖数组中直接使用一个会不断变化的值,可能会导致无限循环的副作用执行。通过记忆化这个值,你可以确保只有当值实际改变时,副作用才会重新运行。
案例

假设我们再App.js中有以下对象和函数,使用了useMemo与useCallback:

  const [isFakeDark, setIsFakeDark] = useState(false);
  const [posts, setPosts] = useState(() =>
    Array.from({ length: 30 }, () => createRandomPost())
  );

    
  const handleAddPost = useCallback(function handleAddPost(post) {
    setPosts((posts) => [post, ...posts]);
  }, []);


  const archiveOptions = useMemo(() => {
    return {
      show: false,
      title: `Post archive in addition to ${posts.length} main posts`,
    };
  }, [posts.length]);


  <Archive
        archiveOptions={archiveOptions}
        onAddPost={handleAddPost}
        setIsFakeDark={setIsFakeDark}
   />

注意下useMemo与useCallback使用方式的细微差别。

 Archive使用memo如下所示:

const Archive = memo(function Archive({ archiveOptions, onAddPost }) {
  // Here we don't need the setter function. We're only using state to store these posts because the callback function passed into useState (which generates the posts) is only called once, on the initial render. So we use this trick as an optimization technique, because if we just used a regular variable, these posts would be re-created on every render. We could also move the posts outside the components, but I wanted to show you this trick 😉
  const [posts] = useState(() =>
    // 💥 WARNING: This might make your computer slow! Try a smaller `length` first
    Array.from({ length: 30000 }, () => createRandomPost())
  );

  const [showArchive, setShowArchive] = useState(archiveOptions.show);

  return (
    <aside>
      <h2>{archiveOptions.title}</h2>
      <button onClick={() => setShowArchive((s) => !s)}>
        {showArchive ? "Hide archive posts" : "Show archive posts"}
      </button>

      {showArchive && (
        <ul>
          {posts.map((post, i) => (
            <li key={i}>
              <p>
                <strong>{post.title}:</strong> {post.body}
              </p>
              <button onClick={() => onAddPost(post)}>Add as new post</button>
            </li>
          ))}
        </ul>
      )}
    </aside>
  );
});

注意: 对于state的set函数来说,它不需要useCallback函数,它本身随着渲染就不会改变地址。

减小Bundle大小 

Bundle与Bundle分别是什么?

代码分割

代码分割(Code splitting)是一种在现代前端工程中常用的优化技术,它允许将应用程序的代码库分割成多个小块(bundles),这些小块可以独立于主代码库进行加载。这种方法特别适用于大型应用程序,可以显著提高加载速度和性能

 路由级别代码分割

转变为

Suspense是一个用于处理异步组件加载的组件。它允许你定义一个边界,当其子组件在加载过程中遇到异步操作(如数据获取)而暂停时,可以显示一个备用内容(fallback)。然后Suspense与React的lazy函数结合使用,实现组件的懒加载。这种方式允许你在组件首次渲染时自动触发组件的加载,同时在加载过程中显示一个占位符。

因为懒加载的元素,当你切换到Login或者Pricing等页面的时候, 对应的js的代码块才刚刚从服务器下载过来,需要时间到达客户端。这个时候suspense组件就起到了作用!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分布式事务的前世今生-纯理论

一个可用的复杂的系统总是从可用的简单系统进化而来。反过来这句话也正确: 从零开始设计的复杂的系统从来都用不了&#xff0c;也没办法让它变的可用。 --John Gal 《系统学》 1975 1. 事务的概念 百科&#xff1a; 事务&#xff08;Transaction&#xff09;&#xff0c;一般是…

k8s-编写CSI插件(3)

1、概述 在 Kubernetes 中&#xff0c;存储插件的开发主要有以下几种方式&#xff1a; CSI插件&#xff1a;Container Storage Interface (CSI) 是 Kubernetes 的标准插件接口&#xff0c;是全新的插件方案&#xff0c;插件和驱动调用通过grpc协议&#xff0c;功能丰富&#x…

Linux系统:网络

目录 一、网络协议 1.网络协议概念 2.协议分层 3.OSI七层模型和TCP/IP五层&#xff08;或四层&#xff09;模型 4.为什么要有网络协议&#xff1f; 5.网络通信协议的原理 二、网络传输的基本流程 1.局域网的网络传输流程 1.MAC地址 2.局域网通信原理&#xff08;以太网…

电子应用设计方案-45:智能火锅系统方案设计

智能火锅系统方案设计 一、引言 随着人们生活水平的提高和对饮食体验的追求&#xff0c;智能火锅系统应运而生。本方案旨在设计一款集智能化控制、高效加热、安全保障和个性化体验于一体的智能火锅系统。 二、系统概述 1. 系统目标 - 实现精准的温度控制&#xff0c;满足不同…

论文概览 |《Urban Analytics and City Science》2023.03 Vol.50 Issue.3

本次给大家整理的是《Environment and Planning B: Urban Analytics and City Science》杂志2023年3月第50卷第3期的论文的题目和摘要&#xff0c;一共包括18篇SCI论文&#xff01; 论文1 A new kind of search 一种新型的搜索 【摘要】 ChatGPT (2022) was first launched o…

jwt简介和在go中的简单使用

什么是 JSON Web 令牌&#xff1f; JSON Web 令牌 &#xff08;JWT&#xff09; 是一种开放标准 &#xff08;RFC 7519&#xff09;&#xff0c;它定义了一种紧凑且自包含的方式&#xff0c;用于将信息作为 JSON 对象在各方之间安全地传输。此信息是经过数字签名的&#xff0c…

机器学习详解(3):线性回归之代码详解

文章目录 1 数据预处理2 构建线性回归模型并绘制回归线初始化方法前向传播&#xff1a;forward_propagation代价函数&#xff1a;cost_function反向传播&#xff1a;backward_propagation参数更新&#xff1a;update_parameters训练方法&#xff1a;train代码运行结果 3 使用Py…

iOS如何自定义一个类似UITextView的本文编辑View

对于IOS涉及文本输入常用的两个View是UITextView和UITextField&#xff0c;一个用于复杂文本输入&#xff0c;一个用于简单文本输入&#xff0c;在大多数开发中涉及文本输入的场景使用这两个View能够满足需求。但是对于富文本编辑相关的开发&#xff0c;这两个View就无法满足自…

唇形同步视频生成工具:Wav2Lip

一、模型介绍 今天介绍一个唇形同步的工具-Wav2Lip&#xff1b;Wav2Lip是一种用于生成唇形同步&#xff08;lip-sync&#xff09;视频的深度学习算法&#xff0c;它能够根据输入的音频流自动为给定的人脸视频添加准确的口型动作。 &#xff08;Paper&#xff09; Wav2Lip模型…

轻量化特征融合 | YOLOv11 引入一种基于增强层间特征相关性的轻量级特征融合网络 | 北理工新作

本改进已同步到Magic框架 摘要—无人机图像中的小目标检测由于分辨率低和背景融合等因素具有挑战性,导致特征信息有限。多尺度特征融合可以通过捕获不同尺度的信息来增强检测,但传统策略效果不佳。简单的连接或加法操作无法充分利用多尺度融合的优势,导致特征之间的相关性不…

数学课上的囚徒问题(2)

#include<bits/stdc.h> using namespace std; int main() {int n; cin>>n;double res0;for(int in/21;i<n;i)res1./i;cout<<fixed<<setprecision(12)<<(1-res); } 赛后看到别人那么短的代码直接破防了&#xff0c;才发现思考起来也不是很简单…

21Java之多线程、线程池、并发、并行

一、多线程常用方法 下面我们演示一下getName()、setName(String name)、currentThread()、sleep(long time)这些方法的使用效果。 public class MyThread extends Thread{public MyThread(String name){super(name); //1.执行父类Thread(String name)构造器&#xff0c;为当前…

HttpClient介绍

1. 介绍 2. 发送Get方式请求 public void httpGetTest() throws Exception{// 创建HttpClient对象CloseableHttpClient httpClient HttpClients.createDefault();// 创建请求方式对象HttpGet httpGet new HttpGet("http://localhost/user/shop/status");// 发送请…

矩阵的乘(包括乘方)和除

矩阵的乘分为两种&#xff1a; 一种是高等代数中对矩阵的乘的定义&#xff1a;可以去这里看看包含矩阵的乘。总的来说&#xff0c;若矩阵 A s ∗ n A_{s*n} As∗n​列数和矩阵 B n ∗ t B_{n*t} Bn∗t​的行数相等&#xff0c;则 A A A和 B B B可相乘&#xff0c;得到一个矩阵 …

智能跳转 - 短链接高级特性详解

看到标题&#xff0c;我只想说短链接工具已经卷疯了。很多人都知道&#xff0c;短链接的基础特性就是将长链接变短&#xff0c;更加简洁美观便于传播推广&#xff1b;高级一点的功能还有数据统计&#xff0c;便于运营进行分析决策&#xff1b;更高级的还能绑定企业自己的域名&a…

离线写博客(失败) - 用Markdown来离线写博客

因为想控制一下用网&#xff0c;但是又有写博客的需求&#xff0c;所以想研究一下离线写博客。 我看CSDN上面好像有很多介绍&#xff0c;Windows Live Writer 啦&#xff0c;Markdown啦&#xff0c;还有一些其他的&#xff0c;我看了一下&#xff0c;好像 Markdown还有点儿靠谱…

第三节、电机定速转动【51单片机-TB6600驱动器-步进电机教程】

摘要&#xff1a;本节介绍用定时器定时的方式&#xff0c;精准控制脉冲时间&#xff0c;从而控制步进电机速度 一、计算过程 1.1 电机每一步的角速度等于走这一步所花费的时间&#xff0c;走一步角度等于步距角&#xff0c;走一步的时间等于一个脉冲的时间 w s t e p t … ……

夏普MX-4608N复印机维修模式进入方法及载体初始化方法

夏普MX-4608N复印机载体型号&#xff08;图&#xff09;&#xff1a; 型 号&#xff1a;载体&#xff08;黑色&#xff09;MX-561CV 净含量&#xff1a;395克 下面图片中分别有载体、刮板、鼓芯、上纸盒搓纸轮一套&#xff0c;均原装正品&#xff1b; 保养周期将至的时候建…

头歌 Linux之线程管理

第1关&#xff1a;创建线程 任务描述 通常我们编写的程序都是单进程&#xff0c;如果在一个进程中没有创建新的线程&#xff0c;则这个单进程程序也就是单线程程序。本关我们将介绍如何在一个进程中创建多个线程。 本关任务&#xff1a;学会使用C语言在Linux系统中使用pthrea…

Docker的彻底删除与重新安装(ubuntu22.04)

Docker的彻底删除与重新安装&#xff08;ubuntu22.04&#xff09; 一、首先我们彻底删除Docker1、删除docker及安装时自动安装的所有包2、删除无用的相关的配置文件3、删除相关插件4、删除docker的相关配置和目录 二、重新安装1、添加 Docker 的官方 GPG 密钥&#xff1a;2、将…