从零开始搭建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应用性能的关键技术之一,它允许将应用的部分代码(通常是组件或模块)推迟到真正需要时再加载。这有助于减少初始加载时间和资源占用,提高应用的响应速度。下面是懒加载的优化原理:

  1. 初始加载的轻量化: 在应用的初始加载阶段,只加载必需的核心代码,以加速首次页面加载。这通常包括应用的骨架结构、导航和一些基本组件。

  2. 按需加载: 一旦初始加载完成,懒加载技术允许您动态加载应用的其他部分,例如某个页面的组件或特定功能模块。这些代码块通常被拆分成小块,每个小块都与一个特定的组件或功能相关联。

  3. 代码分割: 为了实现懒加载,您可以使用Webpack等打包工具的代码分割功能。通过将应用拆分成多个代码块,您可以在需要时单独加载每个代码块,而不必加载整个应用。

  4. 动态导入: 在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应用程序时。通过配置这些选项,您可以控制输出文件的生成结构,确保资源文件可以正确加载,并根据具体项目需求自定义文件名称和目录结构。

上述代码中,我们在 filenamechunkFilename 中添加了 [contenthash:8] 部分,这将在文件名中包含内容哈希。:8表示只使用前8位哈希字符,在项目中,可以根据需要选择不同的长度。以下是关于不同哈希类型的表格说明,以便理解它们的原理和用途:

哈希类型原理和用途
hashhash 是Webpack提供的默认哈希类型。它会生成一个相对短的哈希值,不具备版本控制的作用。它会在每次构建时生成相同的哈希,除非文件内容发生变化。不适合用于长期的缓存管理或版本控制。
chunkhashchunkhash 基于模块的内容生成哈希,因此每个模块的内容都不同,如果模块的内容发生变化,对应的哈希也会更改。适合用于缓存管理,确保只有在相关模块发生变化时才会更新缓存。
contenthashcontenthash 是基于文件内容生成的哈希,适用于长期的缓存管理和版本控制。当文件内容发生变化时,对应的哈希也会随之更改,确保浏览器可以识别并加载新的文件版本。

这些不同类型的哈希用于控制输出文件的名称,并根据不同的需求进行缓存管理和版本控制。chunkhashcontenthash 特别适用于生产环境,以确保只有在模块或文件内容发生变化时才更新缓存,提高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应用的性能需要仔细考虑和定制,以满足特定项目的需求。

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

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

相关文章

大厂秋招真题【模拟】阿里蚂蚁20231010秋招T2-奇偶操作

题目描述与示例 题目描述 小红有一个长度为n的数组a&#xff0c;她将对数组进行m次操作&#xff0c;每次操作有两种类型&#xff1a; 将数组中所有值为奇数的元素加上x将数组中所有值为偶数的元素加上x 请你输出m次操作后的数组 输入描述 第一行两个整数n和m&#xff0c;…

Android平台上执行C/C++可执行程序,linux系统编程开发,NDK开发前奏。

Android平台上执行C/C可执行程序&#xff0c;linux系统编程开发&#xff0c;NDK开发前奏准备。 1.下载NDK&#xff0c;搭建NDK开发环境 下载地址 https://developer.android.com/ndk/downloads 下载过程中点击下面箭头的地方&#xff0c;点击鼠标右键&#xff0c;复制好下载…

MyBatis与SQL实用技巧 实用语法

数据库SQL技巧 数值转字符 <select id"getMaterialsList" resultType"java.util.Map">selectmaterial_id materialId,material_name materialName,unit, specification, CONVERT(unit_price,CHAR) unitPricefrom trace_agriculture_materialwhere …

2. Spark报错,Task is Failed,errorMsg: FileNotFoundException xxxx

完整报错信息 21304, Task is Failed,errorMsg: FileNotFoundException: File does not exist: hdfs://xxxx-bigdata-nameservice/user/hive/warehouse/edw_ic.db/xxxx/part-00000-c8a718b3-54b3-42de-b36c-d6eedefd2e02-c000.snappy.parquet It is possible the xxx报错场景 …

【React-Native开发3D应用】React Native加载GLB格式3D模型并打包至Android手机端

【React-Native开发3D应用】React Native加载GLB格式3D模型并打包至Android手机端 【加载3D模型】**React Native上如何加载glb格式的模型**第零步&#xff0c;选择相关模型第一步&#xff0c;导入相关模型加载库第二步&#xff0c;自定义GLB模型加载钩子第三步&#xff0c;借助…

this是指向的哪个全局变量,改变this指向的方法有几种?

在JavaScript中&#xff0c;this关键字指向当前执行上下文中的对象。它的具体指向取决于函数的调用方式。 改变this指向的方法有四种&#xff1a; 1.使用call()方法&#xff1a;call()方法在调用函数时将指定的对象作为参数传递进去&#xff0c;从而改变函数的this指向。用法示…

现一个智能的SQL编辑器

补给资料 管注公众号&#xff1a;码农补给站 前言 目前我司的多个产品中都支持在线编辑 SQL 来生成对应的任务。为了优化用户体验&#xff0c;在使用 MonacoEditor 为编辑器的基础上&#xff0c;我们还支持了如下几个重要功能&#xff1a; 多种 SQL 的语法高亮多种 S…

React路由与导航

目录 前言&#xff1a; 什么是React路由&#xff1f; 导航和页面切换 路由参数和动态路由 路由守卫和权限控制 总结 前言&#xff1a; React是一个流行的JavaScript库&#xff0c;用于构建用户界面。在使用React开发Web应用程序时&#xff0c;路由和导航是必不可少的功能…

C语言初学1:详解#include <stdio.h>

一、概念 #include <stdio.h> 称为编译预处理命令&#xff0c;它在告诉C编译器在编译时包含stdio.h文件&#xff0c;如果在代码中&#xff0c;调用了这个头文件中的函数或者宏定义&#xff0c;则需引用该头文件。 二、作用 stdio.h是c语言中的标准输入输出的头文件&am…

【MATLAB源码-第69期】基于matlab的LDPC码,turbo码,卷积码误码率对比,码率均为1/3,BPSK调制。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 本文章介绍了卷积码、Turbo码和LDPC码。以相同的码率仿真这三种编码&#xff0c;并对比其误码率性能 信源输出的数据符号&#xff08;二进制&#xff09;是相互独立和等概率的&#xff1b; 信道是加性白高斯噪声信道&#…

qframework 架构 (作者:凉鞋)使用笔记

一些准则&#xff1a; 根据VIEW->SYSTEM->MODEL的分层架构 初始架构&#xff1a; app. using FrameworkDesign;namespace ShootingEditor2D&#xff08;项目的命名空间&#xff09; {public class ShootingEditor2D &#xff08;游戏名称&#xff09;: Architecture&l…

Android Studio——android项目运行main()函数

报错&#xff1a; 解决&#xff1a; 如图&#xff0c;在 .idea 的 gradle.xml 中标注的位置增加如下一行代码即可<option name"delegatedBuild" value"false" />

LinuxMySql

结构化查询语言 DDL&#xff08;数据定义语言&#xff09; 删除数据库drop database DbName; 创建数据库create database DbName; 使用数据库use DbName; 查看创建数据库语句以及字符编码show create database 43th; 修改数据库属性&#xff08;字符编码改为gbk&#xff09;…

HarmonyOS应用开发-常用组件与布局

基础组件 Text 功能&#xff1a;用于显示文本内容。属性&#xff1a;可以设置文本颜色、字体大小、字体样式、字体粗细和字体族。 参数名称参数类型描述fontColorResourceColor设置文本颜色。fontSizeLength | Resource设置文本尺寸&#xff0c;Length为number类型时&#x…

CSS3实现动态旋转加载样式

要使用 CSS3 创建一个动态旋转加载样式&#xff0c;可以使用 CSS 动画和旋转变换。下面是一个简单的示例&#xff1a; HTML&#xff1a; <div class"loader"></div> CSS&#xff1a; .loader {width: 50px;height: 50px;border: 4px solid #3498db;b…

HR人才测评,采用线上测评做春招秋招

从人力资源管理的工作&#xff0c;已经有好些年了&#xff0c;我只想说这不是一个有创意和创造性的工作&#xff0c;因为大部分时间我都在从事数据方面的工作。关于公司内部的文案工作先且不说&#xff0c;这里分享下我做招聘工作的过程。 每年春秋两季的校招&#xff0c;算是…

基于单片机的多层电梯控制仿真系统

**单片机设计介绍&#xff0c; 基于单片机的多层电梯控制仿真系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的多层电梯控制仿真系统是一个复杂的系统&#xff0c;它需要结合单片机技术、控制理论、电子技术以及人…

RabbitMQ 系列教程

一、RabbitMQ 部署及配置详解(集群部署) 二、RabbitMQ 部署及配置详解 (单机) 三、RabbitMQ 详解及实例&#xff08;含错误信息处理&#xff09; 四、RabbitMq死信队列及其处理方案 五、RabbitMQ Java开发教程—官方原版 六、RabbitMQ Java开发教程&#xff08;二&#x…

虚拟机复制后,无法ping通问题解决

虚拟机复制后&#xff0c;无法ping通问题解决 可能出现的现象 ssh工具连接不上虚拟机&#xff1b;虚拟机ping不通外网或者ping不通内网其它虚拟机&#xff1b; 原因 原虚拟机和新复制出来的虚拟机的ip地址重复&#xff1b;原虚拟机和新复制出来的虚拟机的MAC地址重复&#…