聊一聊 webpack5性能优化有哪些?

news2024/11/25 2:51:26

介绍

此文章基于webpack5来阐述

webpack性能优化较多,可以对其进行分类

  1. 优化打包速度,开发或者构建时优化打包速度(比如exclude、catch等)
  2. 优化打包后的结果,上线时的优化(比如分包处理、减小包体积、CDN服务等)

大多数情况下,我们会更侧重于优化打包后的结果,这对于线上产品的影响更大。

优化构建速度

对于 优化构建速度 会从 定向查找减少执行构建的模块合理使用缓存并行构建以提升总体速度并行压缩提高构建效率几个方面入手。

定向查找

resolve.modules

webpackresolve.modules 配置用于指定 webpack 去哪些目录下寻找第三方模块。其默认值是 ['node_modules']webpack 在寻找的时候,会先去当前目录的 ./node_modules 下去查找,没有找到就会再去上一级目录 ../node_modules 中去找,直到找到为止。

所以如果我们项目的第三方依赖模块放置的位置没有变更的话,可以使用绝对路径减少查找的时间,配置如下:

module.export = {
  resolve: {
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    // __dirname 表示当前工作目录,也就是项目根目录
    modules: [path.resolve(__dirname, 'node_modules')]
  }
}

resolve.extensions

extensions 是我们常用的一个配置,适用于指定在导入语句没有带文件后缀时,可以按照配置的列表,自动补上后缀。我们应该根据我们项目中文件的实际使用情况设置后缀列表,将使用频率高的放在前面、同时后缀列表也要尽可能的少,减少没有必要的匹配。同时,我们在源码中写导入语句的时候,尽量带上后缀,避免查找匹配浪费时间。

同时,我们在源码中写导入语句的时候,尽量带上后缀,避免查找过程。

module.export = {
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  }
}

extensions的默认值是['.js', '.json', '.wasm'],所以不写文件后缀的话,它会依次匹配'.js', '.json', '.wasm',如果都没匹配上的话才会报错。

减少执行构建的模块

合理配置 loader 的 include、exclude

loader 对文件的转换是个耗时的操作,并且 loader 的配置会批量命中多个文件,所以需要根据自己的项目尽可能的精准命中哪些文件是需要被 loader 处理的。

webpack 提供了 test、include、exclude 三个配置项来命中 loader 。

比如,我们只想对根目录 src 下的 js 文件使用 babel-loader 进行处理可以这样设置:

const path = require('path');
module.exports = {
  //...
  module: {
    rules: [
      {
        test: /.jsx?$/,
        use: ['babel-loader'],
        include: [path.resolve(__dirname, 'src')]
      }
    ]
  },
}

或者我们想排除node_modules目录下的js使用 babel-loader 进行处理可以这样设置:

const path = require('path');
module.exports = {
  //...
  module: {
    rules: [
      {
        test: /.jsx?$/,
        use: ['babel-loader'],
        exclude: /node_modules/, //排除 node_modules 目录
      }
    ]
  },
}

合理使用缓存

在优化的方案中,缓存也是其中重要的一环。在构建过程中,可以通过使用缓存提升二次打包速度。

babel-loader 开启缓存

cacheDirectory 的默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。

module.exports = {
  module: {
    rules: [
      {
        test: /.jsx?$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          }
        ]
      }
    ]
  }
}

cache-loader

没有缓存配置的loader该怎么使用缓存呢?那就得借助 cache-loader

其使用如下:

module.exports = {
  module: {
    rules: [
      {
        test: /.jsx?$/,
        use: [
          'cache-loader', //需要安装
          "babel-loader"
        ],
      }
    ]
  }
}

webpack5 配置 cache.type

webpack5新增的cache属性,可以开启磁盘缓存,默认将编译结果缓存在 node_modules/.cache/webpack目录下。

cache 会在开发 模式被设置成 type: 'memory' 而且在 生产 模式中被禁用。 cache: truecache: { type: 'memory' } 配置作用一致。 传入 false 会禁用缓存。

经过配置,再次构建可以看到.cache/webpack 下生成了缓存文件

当然缓存也是不能盲目使用,也是需要斟酌,因为保存和读取这些缓存文件也会有一些时间开销,所以建议只对性能开销较大的 loader 采用改缓存优化。

并行构建以提升总体速度

默认情况下,webpack 是单线程模型,一次只能处理一个任务,在文件过多时会导致构建速度变慢。所以在减少了需要执行构建的模块和降低了单个模块的构建速度之外,我们还可以并行构建,让 webpack 同时处理多个任务,发挥多核 CPU 的优势。

Thread-loader

Thread-loader ,会创建多个 worker 池进行并发执行构建任务,但是使用起来更为简单。只要将这个 loader 放置在其他 loader之前, 放置在这个 Thread-loader 之后的 loader 就会在一个单独的 worker 池(worker pool) 中运行。

其使用如下:

module.exports = {
  module: {
    rules: [
      {
        test: /.jsx?$/,
        use: [
          // 开启多进程打包。 
          {
            loader: 'thread-loader', // 需要安装
            options: {
              workers: 3 // 进程3个
            }
          },
          {
            loader: 'babel-loader',
          }
        ]
      }
    ]
  }
}

在 webpack 官网 中也有提示,每个 worker 都是一个单独的有 600ms 限制的 node.js 进程。同时跨进程的数据交换也会被限制。所以建议仅在耗时的 loader 上使用。

只有在代码量很多的时候开启多进程构建才会有明显的提升,如果项目很简单,代码量少可能会适得其反。所以使用前需要斟酌,不要为了优化而优化。

并行压缩提高构建效率

前面说了并行构建,下面来说说并行压缩。

TerserWebpackPlugin 开启 paralle

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({ parallel: true }), // 默认已经开启,其实无需设置
    ],
  },
};

优化构建结果

对于 优化构建结果 我们可以从 压缩代码按需加载预加载Code SplittingTree ShakingGzip作用提升几个方面入手。

压缩代码

压缩 html

压缩 html 使用的还是 html-webpack-plugin 插件。该插件支持配置一个 minify 对象,用来配置压缩 html

module.export = {
  plugins: [
    new HtmlWebpackPlugin({
      // 动态生成 html 文件
      template: "./index.html",
      minify: {
        // 压缩HTML
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空⽩符与换⾏符
        minifyCSS: true // 压缩内联css
      },
    })
  ]
}

压缩 css

webpack5中推荐使用的是 css-minimizer-webpack-plugin。

首先我们在入口文件里面引入test.css文件

import "./test.css";

test.css文件内容如下

.box {
  background-color: red;
}

.item {
  color: black;
}

配置方式:

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

optimization: {
  // 是否需要压缩
  minimize: true, // 需要开启
  // 配置压缩工具
  minimizer: [
    // 添加 css 压缩配置
    new CssMinimizerPlugin({}), // 需要安装
  ],
},

重新构建,会单独生成了 main.css 文件,并对css进行了压缩。

压缩 js

目前的主流还是terser-webpack-plugin,在webpack5生产环境中(mode=production),已默认开启。

如果你不想使用terser-webpack-plugin插件的默认配置,想自定义,也是支持的,可以直接引入terser-webpack-plugin进行自定义配置即可。

const TerserPlugin = require("terser-webpack-plugin"); // webpack5内置,不需要再单独安装

optimization: {
  // 是否需要压缩
  minimize: true,
  // 配置压缩工具
  minimizer: [
    new TerserPlugin({// 在这里自定义配置}),
  ],
},

按需加载

很多时候不需要一次性加载所有的JS文件,而应该在不同阶段去加载所需要的代码。webpack内置了强大的分割代码的功能可以实现按需加载。

比如,我们在点击了某个按钮之后,才需要使用使用对应的JS文件中的代码,我们可以使用 import() 语法按需引入

// index.js

document.getElementById('btn1').onclick = function() {
  import('./impModule.js').then(fn => fn.default());
}

impModule.js

export default () => {
  console.log("我是懒加载模块");
};

我们打包之后,动态加载的模块会单独生成一个js文件。

在这里插入图片描述

页面首次加载的时候并不会加载该js文件,而是当我们需要使用到的时候才会进行加载。我们在页面上看看效果。

在默认情况下打包出来的文件名是路径的组合,比如上面的src_impModule.js,如果你不想使用这个名字,想通俗易懂可以在import里面配置 webpackChunkName魔法注释。

比如上面的例子,我们配置ebpackChunkName: "btnChunk"

// index.js

document.getElementById('btn1').onclick = function() {
  import(/* webpackChunkName: "btnChunk" */ './impModule.js').then(fn => fn.default());
}

再次构建可以看到,构建出来的文件名就是我们事先定义好的名称

在这里插入图片描述

预加载(prefetch 和 preload)

上面说的代码懒加载在使用的时候才去加载是会提升页面性能,但是如果懒加载的模块比较大,当点击的时候再去加载的话无疑会让用户等待时间加长。

可以利用浏览器空闲时候去加载这些切分出来的模块,那就是prefetch 和 preload

prefetch和preload的概念

prefetch(预取):将来可能需要一些模块资源,在核心代码加载完成之后浏览器空闲的时候再去加载需要用到的模块代码。

preload(预加载):当前核心代码加载期间可能需要模块资源,其是和核心代码文件一起去加载的。

prefetch

我们将上面的例子稍微改下,加个注释/* webpackPrefetch: true */

// index.js

document.getElementById("btn1").onclick = async () => {
  const imp = await import(/* webpackPrefetch: true */ "./impModule.js");
  imp.default();
};

上面的代码的意思是当我们主要的核心代码加载完成,浏览器有空闲的时候,浏览器就会帮我们自动的去下载impModule.js

head里面,懒加载模块会被直接引入了,并且会添加rel='prefetch'

在这里插入图片描述

preload

prefetch 与 preload 的区别

  1. preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  2. preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
  3. preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
  4. 浏览器支持程度不同,需要注意。

Code Splitting (代码分割)

要理解webpack的提出的几个概念,module、chunk和bundle。

module:每个import引入的文件就是一个模块

chunk:当module源文件传到webpack进行打包时,webpack会根据文件引用关系生成chunk

bundle:是对chunk进行压缩、分割等处理后的产物

在这里插入图片描述

SplitChunksPlugin

可以阅读我的另外一篇文章,里面讲述了一些拆包策略 SplitChunksPlugin

MiniCssExtractPlugin

可以利用 mini-css-extract-plugin 插件,将我们的css代码分离出来。

接下来我们实操下。

创建样式文件

// index.less
.a {
  background-color: aqua;
}

.b {
  font-size: 18px;
}

配置mini-css-extract-plugin插件

//webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

// ...
{
  test: /.less?$/,
  use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
  exclude: /node_modules/, //排除 node_modules 目录
},

plugins: [
  // ...
  new MiniCssExtractPlugin(),
  }),
],

css代码的分离优化原理其实和js是一样的。第一就是拆出来利用浏览器并发请求特性进行快速加载,其次就是多页面如果用到了相同样式能进行复用。

注意,此插件为每个包含 cssjs 文件创建一个单独的 css 文件

这句话的意思就是,css代码的分割不是以引入了多少个css文件构建后就有多少个css文件,而是根据你的js包来的,如果构建后你的js包只有一个那么css包也只会有一个,而不管你源代码里引入了多少个css文件。

Tree Shaking

利用 ES Module 静态分析的特点来检测模块内容的导出、导入以及被使用的情况,保留被使用的代码,消除不会被执行和没有副作用(Side Effect) 的 死代码。

Tree Shaking 最先出现在rollup中,rollup 是在编译打包过程中分析程序流,得益于 ES6 静态模块(exports 和 imports 不能在运行时修改),我们在打包时就可以确定哪些代码是我们需要的。

webpack 本身在打包时只能标记未使用的代码而不移除,而识别代码未使用标记并完成 tree-shaking 的 其实是 UglifyJS、terser 这类压缩代码的工具。简单来说,就是压缩工具读取 webpack 打包结果,在压缩之前移除 bundle 中未使用的代码

webpack 中的 sideEffects 表示是否要开启识别 package.json 中 sideEffects 标记的功能,而 package.json 中的 sideEffects 是为了告知打包工具该模块是否包含副作用或者包含哪些副作用。

Gzip

前端除了在打包的时候将无用的代码或者 console、注释剔除之外。还可以使用 Gzip 对资源进行进一步压缩。

  1. 当用户访问 web 站点的时候,会在 request header 中设置 accept-encoding: gzip,表明浏览器是否支持 Gzip
  2. 服务器在收到请求后,判断如果需要返回 Gzip 压缩后的文件那么服务器就会先将我们的 JS\CSS 等其他资源文件进行 Gzip 压缩后再传输到客户端,同时将 response headers 设置 content-encoding:gzip。反之,则返回源文件。
  3. 浏览器在接收到服务器返回的文件后,判断服务端返回的内容是否为压缩过的内容,是的话则进行解压操作。
const CompressionWebpackPlugin = require("compression-webpack-plugin"); // 需要安装

module.exports = {
  plugins: [
    new CompressionWebpackPlugin()
  ]
}

配置好我们再来构建,会生成资源的.gz文件

当然在Nginx上也可以在配置文件中启用 gzip 压缩

作用提升 (Scope Hoisting)

Scope Hoisting 作用域提升,在 JavaScript 中,也有类似的概念,“变量提升”、“函数提升”,JavaScript 会把函数和变量声明提升到当前作用域的顶部,Scope Hoisting 也是类似。webpack 会把引入的 js 文件“提升”顶部。

在没有使用 Scope Hoisting 的时候,webpack 的打包文件会将各个模块分开使用 __webpack_require__ 导入,在使用了 Scope Hoisting 之后,就会把需要导入的文件直接移入使用模块的顶部。这样做的好处有

  • 代码中函数声明和引用语句减少,减少代码体积
  • 不用多次使用 __webpack_require__ 调用模块,运行速度会的得以提升。

所以,Scope Hoisting 可以让 webpack 打包出来的代码文件体积更小,运行更快。

因为 Scope Hoisting 需要分析模块之间的依赖关系,所以源码必须采用 ES6 模块化语法。也就是说如果你使用非 ES6 模块或者使用 import() 动态导入的话,则不会有 Scope Hoisting

Scope Hoistingwebpack 内置功能,只需要在plugins里面使用即可

module.exports = {
  plugins: [
    // 开启 Scope Hoisting 功能
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
}

不过生产环境下 Scope Hoisting 功能是默认开启的,不用再额外处理。

常用分析工具

时间分析工具 speed-measure-webpack-plugin

speed-measure-webpack-plugin 这个插件分析整个打包的总耗时,以及每一个loader 和每一个 plugins 构建所耗费的时间,从而帮助我们快速定位到可以优化 Webpack 的配置。

在这里插入图片描述

使用如下

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); // 需要安装
const smp = new SpeedMeasurePlugin();

module.exports = () => smp.wrap(config); // 使用smp包裹webpack的配置

构建结果分析工具 webpack-bundle-analyze

webpack-bundle-analyzer 能可视化的反映

  1. 打包出的文件中都包含了什么;
  2. 每个文件的尺寸在总体中的占比,哪些文件尺寸大;
  3. 模块之间的包含关系;
  4. 是否有重复的依赖项,是否存在一个库在多个文件中重复? 或者包中是否具有同一库的多个版本?
  5. 每个文件的压缩后的大小。

使用如下:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 需要安装

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

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

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

相关文章

什么是安全生产痕迹化管理?如何做到生产过程中全程痕迹化管理?

安全生产痕迹化管理,简单来说,就是通过记录一些“信息”来确保安全工作的进展。这些方法包括记会议内容、写安全日记、拍照片、签字盖章、指纹识别、面部识别还有手机定位等。记录下来的文字、图片、数据和视频,就像一个个“脚印”&#xff0…

“免费”制作中国式报表的工具横空出世,内置丰富图表组件!

一.报表制作的烦恼 报表是我们日常工作中的好伙伴,它在企业管理和决策过程中扮演着重要角色,能够清晰直观地展示数据,让关键信息一目了然。 然而,无论是使用传统的手工报表还是基于软件的普通报表操作,都存在不便之处…

clickhouse sql 语法参考

clickhouse sql 语法参考 1. select1.1 将结果中的某些列与 re2 正则表达式匹配,可以使用 COLUMNS 表1.2 ARRAY JOIN - 数组数据平铺1.3 LEFT ARRAY JOIN 2. create2.1 分布式创建数据库2.2 分布式创建复制表2.4 CREATE TABLE [IF NOT EXISTS] [db.]table_name ENGI…

layui table 重新设置表格的高度

在layui的table模块中,如果使用table.render({})渲染了一个表格实例时,确定了height配置,后续用table.resize(id)方法重置表格尺寸时,表格的高度是不会变化的(如果我的理解没有错的话)。 有时我们希望根据…

ChatGPT在办公与科研中有怎样的应用?又是如何做论文撰写、数据分析、机器学习、深度学习及AI绘图

2022年11月30日,可能将成为一个改变人类历史的日子——美国人工智能开发机构OpenAI推出了聊天机器人ChatGPT-3.5,将人工智能的发展推向了一个新的高度。2023年11月7日,OpenAI首届开发者大会被称为“科技界的春晚”,吸引了全球广大…

8.1 字符串中等 43 Multiply Strings 38 Count and Say

43 Multiply Strings【默写】 那个难点我就没想先解决,原本想法是先想其他思路,但也没想出。本来只想chat一下使用longlong数据类型直接stoi()得不得行,然后就看到了答案,直接一个默写的大动作。但这道题确实考察的是还原乘法&…

C++(入门2)

承接上C(入门1):CSDN 引用:(类似指针但不同于指针,后续会将两则不同) 概念 引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间…

c语言第六天笔记

分支结构 分支结构:又被称之为选择结构 概念 选择结构:根据条件成立与否,选择相应的操作。 条件构建 关系表达式:含有关系运算符的表达式(>,,) 逻辑表达式:含有逻辑运算符的表达式&…

网络安全相关工作必须要有证书吗?

在当今数字化时代,网络安全已成为至关重要的领域。然而,对于从事网络安全相关工作的人员来说,证书是否是必不可少的呢? 一、网络安全证书的重要性 网络安全证书在一定程度上能够证明从业者具备相关的知识和技能。例如,CISP 作为国…

基于LLM开发AI应用竟如此简单

一、什么是LLM 随着人工智能技术的不断发展,越来越多的企业和机构开始将其应用于各个领域。其中,基于语言模型的人工智能技术(LLM)在自然语言处理、文本生成等方面表现出色,被广泛应用于各种场景中。 LLM是一种基于大…

你们要的“轮子”来了!67 个仓颉语言三方库正式公开!

01 Cangjie-TPC社区简介 Cangjie-TPC(Third Party Components)用于汇集基于仓颉编程语言开发的开源三方库,帮助开发者方便、快捷、高质量构建仓颉程序。 Cangjie-TPC社区联合软通动力、宝兰德、普元、上汽以及社区开发者共同完成第一批常用…

快速设置 terminator 透明背景

看图,按步骤设置后⭐重启一个终端则为透明效果 效果展示:

vscode+platformio开发小技巧

使用vscodeplatformio开发,具体安装配置文章很多,这里分享一些方便使用的小技巧,让使用体验在不增加学习成本的情况下更加丝滑。 1、配置依赖库 在使用vscode开发前,arduino环境遗留了一些库文件,这些第三方库可以通…

(20240801)矿山固废基胶凝材料及混凝土中文期刊整理

一、篇名:固废 级别:EI + 篇名:固废混凝土/水泥/胶砂/胶凝材料 级别:EI

Flat Ads资讯:Meta、Google、TikTok 7月产品政策速递

Flat Ads拥有全球媒介采买(MediaBuy)业务,为方便广告主及时了解大媒体最新政策,Flat Ads将整理大媒体产品更新月报,欢迎大家关注我们及时了解最新行业动向。 一、Meta 1、Reels 应用推广现可突出显示应用评分、点评和下载量 为了不断优化 Instagram 上的广告体验和广告表现,…

攻防世界之《这个按钮做什么》题解

下载解压后,发现只有一个文件。 放入exeinfope软件里看看 根据activity猜测可能是安卓软件,修改文件后缀为.apk 然后用模拟器打开这个软件并会自动安装。 打开软件界面如下: 看得出来只有一个密码输入框,应该找到对应的密码就会…

【游戏引擎之路】登神长阶(八)——Python之旅行,休息一下,去看看新世界

5月20日-6月4日:攻克2D物理引擎。 6月4日-6月13日:攻克《3D数学基础》。 6月13日-6月20日:攻克《3D图形教程》。 6月21日-6月22日:攻克《Raycasting游戏教程》。 6月23日-7月1日:攻克《Windows游戏编程大师技巧》。 7月…

数据结构(其二)--线性表(其二)

目录 5.栈 5.1 栈的基本操作 5.2 各种栈 (1).顺序栈 i.普通顺序栈 ii.共享栈 iii.关于销毁 (2).链栈 6.队列 6.1 队列的基本操作 6.2 各种队列 (1).循环队列 i.代码 ii.另外一种写法 (…

滴滴官宣潘展乐为滴滴网约车“快”乐大使

近日,滴滴宣布邀请游泳运动员潘展乐成为滴滴出行网约车“快”乐大使,同时开展打车确定性体验攻坚计划,上线3分钟无车赔活动,为司机发放高温补贴、流水加速卡等多重奖励,共同为用户提供更快、更便捷的出行体验。 作为男…

机器学习-29-多变量异常检测在区域供热系统中的应用(实战)

参考多变量异常检测:工业数据分析中的异常检测技术 1 多变量异常检测 在工业数据分析需求中,异常检测是数据分析和监控(如系统操作错误、异常情况、潜在和实际故障等)的关键技术。与传统的单变量异常检测(逐一检查每个变量/指标)不同,多变量异常检测考虑了多个变量之间…