从零开始搭建React+TypeScript+webpack开发环境-性能优化
前言
当我们开发React应用时,性能始终是一个重要的考虑因素。随着应用规模的增长,React组件的数量和复杂性也会相应增加,这可能会导致性能问题的出现。在这篇博文中,我们将探讨如何通过一系列的技巧和最佳实践来优化React应用的性能,以确保用户获得更快的加载时间和更流畅的交互体验。
React是一个强大的JavaScript库,它使我们能够轻松构建交互性强的用户界面。然而,使用不当或忽略性能方面的问题可能导致页面加载缓慢、响应迟钝,甚至影响用户的满意度。因此,了解如何优化React应用的性能对于开发者来说是至关重要的。
在本文中,我们将深入研究React性能优化的各个方面,包括组件渲染优化、状态管理、代码拆分、懒加载等关键概念。我们还将介绍一些常见的工具和技术,以帮助您识别和解决性能瓶颈,从而提高应用的效率。
组件渲染优化
使用Pure Components
Pure Components
是 React 中的一种特殊组件,它会自动进行浅比较(shallow comparison)来确定是否需要重新渲染。这可以减少不必要的渲染,提高性能。
import React, { PureComponent } from 'react';class MyComponent extends PureComponent {// ...
}
使用React.memo
React.memo
是一个高阶组件,它可以包裹函数式组件,用于记忆组件的渲染结果,只有在其属性发生变化时才重新渲染。
import React from 'react';const MyComponent = React.memo(function MyComponent(props) {// ...
});
控制组件重新渲染
-
shouldComponentUpdate
class MyComponent extends React.Component {shouldComponentUpdate(nextProps, nextState) {// 根据新的props和state来决定是否重新渲染return this.props.someProp !== nextProps.someProp;}render() {// ...} }
-
useMemo
import React, { useMemo } from 'react';function MyComponent({ someProp }) {const memoizedValue = useMemo(() => computeExpensiveValue(someProp), [someProp]);return <div>{memoizedValue}</div>; }
组件分割
当一个页面太大, 所以逻辑全部写在这个大组件时, 每次页面状态发生变化时, 组件会重现渲染,由于 render 内执行代码逻辑很多.所以会参数很严重的cpu资源占用。
建议把大页面或者代码多大页面拆分组件去写, 拆分组件不是只为了提组件复用性, 减少代码冗余。
合理的拆分,还可以优化页面性能,和提高代码的可维护性。
优化前
function CounterDisplay() {let counter = Counter.useContainer()return (<div><button onClick={counter.decrement}>-</button><p>You clicked {counter.count} times</p><button onClick={counter.increment}>+</button><div><div><div><div>SUPER EXPENSIVE RENDERING STUFF</div></div></div></div></div>)
}
优化后
function ExpensiveComponent() {return (<div><div><div><div>SUPER EXPENSIVE RENDERING STUFF</div></div></div></div>)
}function CounterDisplay() {let counter = Counter.useContainer()return (<div><button onClick={counter.decrement}>-</button><p>You clicked {counter.count} times</p><button onClick={counter.increment}>+</button><ExpensiveComponent /></div>)
}
使用React Hooks优化组件
使用 useMemo()
优化耗时的操作
优化前
function CounterDisplay(props) {let counter = Counter.useContainer()// 每次 `counter` 改变都要重新计算这个值,非常耗时let expensiveValue = expensiveComputation(props.input)return (<div><button onClick={counter.decrement}>-</button><p>You clicked {counter.count} times</p><button onClick={counter.increment}>+</button></div>)
}
优化后
function CounterDisplay(props) {let counter = Counter.useContainer()// 仅在输入更改时重新计算这个值let expensiveValue = useMemo(() => {return expensiveComputation(props.input)}, [props.input])return (<div><button onClick={counter.decrement}>-</button><p>You clicked {counter.count} times</p><button onClick={counter.increment}>+</button></div>)
}
使用 React.memo()
、useCallback()
减少重新渲染次数
优化前
function useCounter() {let [count, setCount] = useState(0)let decrement = () => setCount(count - 1)let increment = () => setCount(count + 1)return { count, decrement, increment }
}let Counter = createContainer(useCounter)function CounterDisplay(props) {let counter = Counter.useContainer()return (<div><button onClick={counter.decrement}>-</button><p>You clicked {counter.count} times</p><button onClick={counter.increment}>+</button></div>)
}
优化后
function useCounter() {let [count, setCount] = useState(0)let decrement = useCallback(() => setCount(count - 1), [count])let increment = useCallback(() => setCount(count + 1), [count])return { count, decrement, increment }
}let Counter = createContainer(useCounter)let CounterDisplayInner = React.memo(props => {return (<div><button onClick={props.decrement}>-</button><p>You clicked {props.count} times</p><button onClick={props.increment}>+</button></div>)
})function CounterDisplay(props) {let counter = Counter.useContainer()return <CounterDisplayInner {...counter} />
}
组件懒加载
懒加载(Lazy Loading)是一项用于优化Web应用性能的关键技术之一,它允许将应用的部分代码(通常是组件或模块)推迟到真正需要时再加载。这有助于减少初始加载时间和资源占用,提高应用的响应速度。下面是懒加载的优化原理:
-
初始加载的轻量化: 在应用的初始加载阶段,只加载必需的核心代码,以加速首次页面加载。这通常包括应用的骨架结构、导航和一些基本组件。
-
按需加载: 一旦初始加载完成,懒加载技术允许您动态加载应用的其他部分,例如某个页面的组件或特定功能模块。这些代码块通常被拆分成小块,每个小块都与一个特定的组件或功能相关联。
-
代码分割: 为了实现懒加载,您可以使用Webpack等打包工具的代码分割功能。通过将应用拆分成多个代码块,您可以在需要时单独加载每个代码块,而不必加载整个应用。
-
动态导入: 在React应用中,您可以使用动态导入(dynamic imports)来实现懒加载。这是通过使用
import()
函数来导入组件或模块的方式,使其成为返回一个Promise的异步操作。
下面是一个示例,演示了React中如何使用懒加载来优化组件的加载:
import React, { lazy, Suspense } from 'react';const LazyComponent = lazy(() => import('./LazyComponent'));function App() {return (<div>{/* 常规加载的组件 */}<RegularComponent />{/* 懒加载的组件 */}<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense></div>);
}
在这个示例中,LazyComponent
组件使用lazy
函数进行懒加载,而Suspense
组件用于处理加载过程中的状态。当用户首次访问应用时,只有RegularComponent
会被立即加载,而LazyComponent
会在需要时才异步加载。fallback
属性用于指定在加载期间显示的占位符。
懒加载的优化原理可以总结为:将应用拆分成多个小块代码,根据需要动态加载这些代码块,以提高初始加载速度和减少资源浪费。这是一种强大的性能优化技术,特别适用于大型和复杂的Web应用。
代码分割与首屏优化
js代码分割
optimization: {splitChunks: {chunks: 'all',name: false,maxSize: 200000,automaticNameDelimiter: '.',},runtimeChunk: {name: 'runtime',},
},
以下是对Webpack配置文件中代码分割和优化部分的各个配置项的作用的表格说明:
配置项 | 作用 |
---|---|
optimization.splitChunks | 代码分割配置 |
chunks: 'all' | 指定要对所有类型的代码块(包括初始块、按需加载块等)进行代码分割。 |
name: false | 不为生成的拆分块创建自定义名称,使用Webpack的默认命名规则。 |
maxSize: 200000 | 设置每个拆分块的最大大小为200KB,如果模块大小超过这个限制,将尝试拆分为更小的块。 |
automaticNameDelimiter: '.' | 自动名称的分隔符,用于将模块名称与生成的块名称组合。 |
runtimeChunk | |
name: 'runtime' | 创建一个包含webpack运行时代码的单独块,以避免在每个拆分块中重复包含运行时代码。 |
这些配置项用于优化代码分割,确保仅加载必要的代码,降低初始加载时间,提高应用性能。根据具体项目需求,可以对这些配置项进行调整和自定义,以获得最佳性能表现。
output: {filename: 'static/js/[name].[contenthash:8].js', // 添加 [contenthash:8] 部分chunkFilename: 'static/js/[name].chunk.[contenthash:8].js', // 添加 [contenthash:8] 部分path: path.resolve(__dirname, 'dist'),publicPath: '/',
},
配置项 | 作用 |
---|---|
filename | 定义输出的主要 JavaScript 文件的名称。通常根据入口点的名称生成文件,放在 dist/static/js 目录下。 |
chunkFilename | 定义异步加载的代码块(chunks)的文件名模板,包括 [name] (代码块名称)和 [id] (代码块唯一标识符)。这些文件通常放在 dist/static/js 目录下。 |
path | 指定输出文件的根目录,通常使用绝对路径,如 path.resolve(__dirname, 'dist') 。 |
publicPath | 定义浏览器中访问这些文件时的公共路径,通常用于指定资源文件的URL前缀,以确保浏览器可以正确加载资源。 |
这些配置项对于定义输出文件的名称、路径和公共访问路径非常重要,特别是在构建Web应用程序时。通过配置这些选项,您可以控制输出文件的生成结构,确保资源文件可以正确加载,并根据具体项目需求自定义文件名称和目录结构。
上述代码中,我们在 filename
和 chunkFilename
中添加了 [contenthash:8]
部分,这将在文件名中包含内容哈希。:8
表示只使用前8位哈希字符,在项目中,可以根据需要选择不同的长度。以下是关于不同哈希类型的表格说明,以便理解它们的原理和用途:
哈希类型 | 原理和用途 |
---|---|
hash | hash 是Webpack提供的默认哈希类型。它会生成一个相对短的哈希值,不具备版本控制的作用。它会在每次构建时生成相同的哈希,除非文件内容发生变化。不适合用于长期的缓存管理或版本控制。 |
chunkhash | chunkhash 基于模块的内容生成哈希,因此每个模块的内容都不同,如果模块的内容发生变化,对应的哈希也会更改。适合用于缓存管理,确保只有在相关模块发生变化时才会更新缓存。 |
contenthash | contenthash 是基于文件内容生成的哈希,适用于长期的缓存管理和版本控制。当文件内容发生变化时,对应的哈希也会随之更改,确保浏览器可以识别并加载新的文件版本。 |
这些不同类型的哈希用于控制输出文件的名称,并根据不同的需求进行缓存管理和版本控制。chunkhash
和 contenthash
特别适用于生产环境,以确保只有在模块或文件内容发生变化时才更新缓存,提高Web应用的性能和稳定性。选择哪种哈希类型取决于您的项目需求和目标。
这样的配置将确保每次文件内容发生变化时,输出文件名都会更改,从而防止浏览器缓存旧版本的文件。这对于有效的缓存管理和版本控制非常有用,特别是在生产环境中。
css分割
对于css,我们可以使用MiniCssExtractPlugin
配置对代码进行分割:
new MiniCssExtractPlugin({filename: 'static/css/[name].[contenthash:8].css', // 添加 [contenthash:8] 部分chunkFilename: 'static/css/[id].[contenthash:8].chunk.css', // 添加 [contenthash:8] 部分
}),
配置项 | 作用 |
---|---|
filename | 定义输出的每个 CSS 文件的名称,通常根据入口点的名称生成文件,放在 static/css 目录下。 [name] 表示 CSS 文件的名称。 |
chunkFilename | 定义按需加载的 CSS 文件的名称,通常用于拆分的 CSS 模块,也放在 static/css 目录下。 [id] 表示 CSS 模块的唯一标识符。 |
这些配置项用于将CSS样式从JavaScript代码中提取出来,并输出到独立的CSS文件中。这样可以更好地管理样式文件,实现缓存和并行加载,提高Web应用的性能。根据具体项目需求,可以自定义这些配置项来满足不同的目录结构和文件命名需求。
结语
在本篇文章中,我们深入探讨了React应用的性能优化,特别关注了代码分割和首屏优化的重要性。我们了解了Webpack配置中的关键部分,包括代码分割的配置,MiniCssExtractPlugin 的设置以及输出文件名的哈希化。
通过使用代码分割,我们可以将应用的代码拆分成多个块,以便在需要时按需加载,从而提高初始加载性能和用户体验。同时,通过哈希化输出文件名,我们可以实现更好的缓存管理和版本控制,确保浏览器始终加载最新的资源文件。
React 18 和Mobx等现代技术的使用使性能优化变得更加容易和强大。最终,优化React应用的性能需要仔细考虑和定制,以满足特定项目的需求。
相关文章:
从零开始搭建React+TypeScript+webpack开发环境-性能优化
前言 当我们开发React应用时,性能始终是一个重要的考虑因素。随着应用规模的增长,React组件的数量和复杂性也会相应增加,这可能会导致性能问题的出现。在这篇博文中,我们将探讨如何通过一系列的技巧和最佳实践来优化React应用的性…...

sCrypt 现在支持 Ordinals 了
比特币社区对 1Sat Ordinals 的接受度正在迅速增加,已有超过 4800 万个铭文被铸造,这一新创新令人兴奋不已。 尽管令人兴奋,但 Ordinals 铭文的工具仍然不发达,这使得使用 Ordinals 进行构建具有挑战性。 更具体地说,缺…...
乌班图搭建 LAMP
搭建 LAMP(Linux、Apache、MySQL、PHP)堆栈是在 Ubuntu 上构建 Web 服务器的常见任务。以下是一些步骤,指导如何在 Ubuntu 上搭建 LAMP 环境: 步骤: 更新系统软件包: 在终端中执行以下命令,确…...

【Unity细节】Unity中的Transform.SetParent还有你不知道的细节
👨💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 😶🌫️收录于专栏:unity细节和bug 😶🌫️优质专栏 ⭐【…...
php生成个性二维码
本篇引用 QRcode PHP QR Code download | SourceForge.net 无需composer即可生成 下载后的类文件是一个压缩包,里边包含很多文件和演示demo,我们只需要里的phpqrcode.php这一个文件就可以生成二维码了。它是一个多个类的集合文件,我们只用…...

css排版—— 一篇优雅的文章(中英文) vs 聊天框的特别排版
文章 <div class"contentBox"><p>这是一篇范文——仅供测试使用</p><p>With the coming of national day, I have a one week holiday. I reallyexpect to it, because it want to have a short trip during these days. Iwill travel to Ji…...

2022最新版-李宏毅机器学习深度学习课程-P46 自监督学习Self-supervised Learning(BERT)
一、概述:自监督学习模型与芝麻街 参数量 ELMO:94MBERT:340MGPT-2:1542MMegatron:8BT5:11BTuring NLG:17BGPT-3:175BSwitch Transformer:1.6T 二、Self-supervised Lear…...

【MySQL习题】各个视频的平均完播率【全网最详细教学】
目录 数据表描述 问题描述 输出示例 解题思路【重点】 正解代码 数据表描述 有以下两张表: 表1:用户-视频互动表tb_user_video_log 数据举例: 说明: uid-用户ID,video_id-视频ID start_time-开始观看时间end_time-结束观…...

Linux Centos配置邮件发送
Linux Centos配置邮件发送 这里使用的是外部发送邮件方式,也就是使用自己的账号发送 第一步 首先要开启STMP授权码,以QQ邮箱为例 配置文件 vim /etc/mail.rc找到之后在最下面添加如下 #邮箱set from3324855376qq.com #默认smtp发送,stmp…...

宋浩高等数学笔记(三)微分中值定理
首先是考研大纲包含的内容: 1.理解并会用罗尔(Rolle)定理、拉格朗日(Lagrange)中值定理和泰勒(Taylor)定理,了解并会用柯西(Cauchy)中值定理. 2.掌握用洛必达法则求未定式极限的方法. 3.理解函数的极值概念,掌握用导数判断函数的单调性和求函…...

华为认证 | 11月底这门HCIP认证即将发布!
非常荣幸地通知您,华为认证HCIP-Storage V5.5(中文版)预计将于2023年11月30日正式对外发布。为了帮助您做好学习、培训和考试计划,现进行预发布通知,请您关注。 01 发布概述 基于“平台生态”战略,围绕“云…...

U-Mail邮件系统安全登录解决方案
企业邮箱是企业对内对外商务往来的主要通信工具,并且企业邮箱里面还包含了大量企业内部隐私信息、商业机密等,很容易成为黑客的攻击目标。其中邮件盗号是企业邮箱遭受攻击的主要形式,一旦企业邮箱密码被黑客盗取,黑客不仅可以利用…...
在Java继承关系中变量访问规则
首先示例代码如下: class A{public int x 0;public int get() {return x;}}class AA extends A{public int x 1; }class AAA extends AA {public int x 2;public int get() {return x;}public static void main(String[] args) {A a new AA();System.out.pri…...
11. 一文快速学懂常用工具——网络工具(下)
本章讲解知识点 引言curltcpdumpwireshark本专栏适合于软件开发刚入职的学生或人士,有一定的编程基础,帮助大家快速掌握工作中必会的工具和指令。本专栏针对面试题答案进行了优化,尽量做到好记、言简意赅。如专栏内容有错漏,欢迎在评论区指出或私聊我更改,一起学习,共同进…...

什么GAN生成对抗网络?生成对抗网络可以干什么?
生成对抗网络(Generative Adversarial Nets,简称GAN)。神经网络分很多种,有普通的前向传播网络,有分析图片的CNN卷积神经网络,有分析系列化数据比如语言、文字的RNN循环神经网络,这些神经网络都是用来输入数据,得到想要的结果,我们看中的是这些神经网络中很好地将数据与…...

采集Prestashop独立站
这是一个用Lua编写的爬虫程序,用于采集Prestashop独立站的内容。爬虫程序使用代理信息:proxy_host: jshk.com.cn。 -- 首先,我们需要导入所需的库 local http require(socket.http) local url require(socket.url)-- 然后,我们…...

通过一道题目带你深入了解WAF特性、PHP超级打印函数、ASCII码chr()对应表等原理[RoarCTF 2019]Easy Calc 1
题目环境: 依此输入以下内容并查看回显结果 11 1’ index.php ls 到这里没思路了 F12查看源代码 一定要仔细看啊,差点没找到,笑哭 访问calc.php文件 果然有点东西 PHP代码审计 error_reporting(0);关闭错误报告 通过GET方式传参的参数num sho…...

类直径树上贪心
http://cplusoj.com/d/senior/p/SS231109C 场上想到枚举点,然后最大值为高,然后可以求最大值。但是感觉计数会重 计数其实不会重,如图中,红色线段显然比蓝色线段优 所以我们枚举3叉点时没错的 #include<bits/stdc.h> usin…...

求职招聘小程序源码系统+社交招聘+多城市招聘 带完整搭建教程
大家好,今天罗峰来给大家分享一款求职招聘小程序源码系统。目前,求职招聘市场在不断变革。传统的招聘网站已经无法满足人们对于高效、便捷、多元化的招聘需求。该系统集求职招聘、社交招聘、多城市招聘等功能于一体,旨在为用户提供更加便捷、…...
Java Web 安全实战:从登录到退出
Java Web 安全实战:从登录到退出 1. 介绍 在当今互联网时代,用户信息安全至关重要。在Java Web开发中,Spring Security是一个强大且灵活的身份验证和访问控制框架,它可以帮助我们构建安全可靠的应用程序。本文将介绍如何使用Spr…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...