webpack提升构建速度

news2025/4/25 2:14:44

目录

    • 配置优化
      • 减少 `resolve` 的解析
      • 把 loader 应用的文件范围缩小
      • 减少 plugin 的消耗
      • 选择合适的 devtool
    • 使用工具
      • thread-loader
      • DLLPlugin
    • 流程优化
      • 拆分构建步骤
      • 拆分项目代码
    • 版本更新
    • 总结

在这里插入图片描述

前端项目随着时间推移和业务发展,页面可能会越来越多,或者功能和业务代码会越来越多,又或者依赖的外部类库会越来越多,这个时候原本不足为道的 webpack 构建时间消耗就会慢慢地进入我们的视野。

构建消耗的时间变长了,如果是使用 CI 服务来做构建,大部分情况下我们无需等待,其实影响不大。但是本地的 webpack 开发环境服务启动时的速度和我们日常开发工作息息相关,在一些性能不是特别突出的设备上(例如便携式笔记本等等),启动时的长时间等待可能会让你越来越受不了。

博主亲身经历的一个项目,使用 webpack 构建的时长可以达到 6 分钟左右,这种场景下,就算用 CI 服务,在遇见需要紧急发布修复问题时,也会让人很抓狂。

也许某一天你负责的项目也会到了需要优化 webpack 构建性能的时候,所以这一章节我们来聊聊如何提升 webpack 的构建速度。

提升 webpack 构建速度本质上就是想办法让 webpack 少干点活,活少了速度自然快了,尽量避免 webpack 去做一些不必要的事情,记得这个主要方向,后续的针对构建速度的优化都是围绕着这一方向展开。

配置优化

减少 resolve 的解析

在前边第三小节我们详细介绍了 webpack 的 resolve 配置,如果我们可以精简 resolve 配置,让 webpack 在查询模块路径时尽可能快速地定位到需要的模块,不做额外的查询工作,那么 webpack 的构建速度也会快一些,下面举个例子,介绍如何在 resolve 这一块做优化:

resolve: {
  modules: [
    path.resolve(__dirname, 'node_modules'), // 使用绝对路径指定 node_modules,不做过多查询
  ],

  // 删除不必要的后缀自动补全,少了文件后缀的自动匹配,即减少了文件路径查询的工作
  // 其他文件可以在编码时指定后缀,如 import('./index.scss')
  extensions: [".js"], 

  // 避免新增默认文件,编码时使用详细的文件路径,代码会更容易解读,也有益于提高构建速度
  mainFiles: ['index'],
},

上述是可以从配置 resolve 下手提升 webpack 构建速度的配置例子。

我们在编码时,如果是使用我们自己本地的代码模块,尽可能编写完整的路径,避免使用目录名,如:import './lib/slider/index.js',这样的代码既清晰易懂,webpack 也不用去多次查询来确定使用哪个文件,一步到位。

把 loader 应用的文件范围缩小

我们在使用 loader 的时候,尽可能把 loader 应用的文件范围缩小,只在最少数必须的代码模块中去使用必要的 loader,例如 node_modules 目录下的其他依赖类库文件,基本就是直接编译好可用的代码,无须再经过 loader 处理了:

rules: [ 
  {
    test: /\.jsx?/,
    include: [ 
      path.resolve(__dirname, 'src'), 
      // 限定只在 src 目录下的 js/jsx 文件需要经 babel-loader 处理
      // 通常我们需要 loader 处理的文件都是存放在 src 目录
    ],
    use: 'babel-loader',
  },
  // ...
],

如上边这个例子,如果没有配置 include,所有的外部依赖模块都经过 Babel 处理的话,构建速度也是会收很大影响的。

减少 plugin 的消耗

webpack 的 plugin 会在构建的过程中加入其它的工作步骤,如果可以的话,适当地移除掉一些没有必要的 plugin。

区分 mode 会让 webpack 的构建更加有针对性,更加高效。例如当 mode 为 development 时,webpack 会避免使用一些提高应用代码加载性能的配置项,如 UglifyJsPlugin,ExtractTextPlugin 等,这样可以更快地启动开发环境的服务,而当 mode 为 production 时,webpack 会避免使用一些便于 debug 的配置,来提升构建时的速度,例如极其消耗性能的 Source Maps 支持。

选择合适的 devtool

前边的内容我们提过,devtool 可以用于配置 webpack 构建出来的 sourcemap 相关的内容,输出详细的 sourcemap 是相当影响 webpack 的构建速度的,所以这里需要做一个取舍,在构建生产环境代码时不输出 sourcemap,而开发环境时一般选用 eval-cheap-source-map 来确保 sourcemap 基本可用的情况下还有着不错的构建速度。

使用工具

thread-loader

thread-loader 是官方提供的一个可以利用多进程加速 loader 执行的 loader,如果项目中 loader 的运行处理占用比较多的时间,例如 babel 或者 typescript 解析和编译的代码量很大,或者 image—webpack-loader 处理图片耗时比较久,那么可以尝试使用 thread-loader 来提速。下边是基础使用的例子:

const threadLoader = require('thread-loader');

const threadLoaderOptions = {
  // 这里填写对应 thread-loader 的配置
  // 预热时的配置和使用 thread-loader 时的配置要一致,所以这里统一使用一个变量来管理
  // 配置参考官方文档:https://github.com/webpack-contrib/thread-loader
}

// thread-loader 的预热,可以加速启动
threadLoader.warmup(threadLoader, [
  'babel-loader',
  // 更多其他需要使用 thread-loader 的 loader
]);

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.jsx?/, // 匹配文件路径的正则表达式,通常我们都是匹配文件类型后缀
        use: [
          // 在你需要的 loader 前加上 thread-loader
          {
            loader: 'thread-loader',
            options: threadLoaderOptions
          },
          {
            loader: 'babel-loader', // 指定使用的 loader
            options: {
              presets: ['@babel/preset-env'],
            },
          },
        ]
      },
      // ...
    ],
}

值得注意的是,由于 thread-loader 新启动进程也需要耗时,所以如果不是运算特别多的 loader 使用 thread-loader 的话可能速度会更慢。

DLLPlugin

DLLPlugin 是 webpack 官方提供的一个插件,也是用来分离代码的,和 optimization.splitChunks有异曲同工之妙,之所以把 DLLPlugin 放到 webpack 构建性能优化这一部分,是因为它的配置相对繁琐,如果项目不涉及性能优化这一块,基本上使用 optimization.splitChunks 即可。

我们来看一下 DLLPlugin 如何使用,使用这个插件时需要额外的一个构建配置,用来打包公共的那一部分代码,举个例子,假设这个额外配置是 webpack.dll.config.js

module.exports = {
  name: 'vendor',
  entry: ['react', 'react-dom'], // 这个例子我们打包 react & react-dom 作为公共类库

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "vendor.js",
    library: "vendor_[hash]" // 打包后对外暴露的类库名称
  },

  plugins: [
    new webpack.DllPlugin({
      name: 'vendor_[hash]',
      path: path.resolve(__dirname, "dist/manifest.json"), // 使用 DLLPlugin 在打包的时候生成一个 manifest 文件
    })
  ],
}

然后就是我们正常的应用构建配置,在那个的基础上添加两个一个新的 webpack.DllReferencePlugin 配置:

module.exports = {
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, 'dist/manifest.json'), 
      // 指定需要用到的 manifest 文件,
      // webpack 会根据这个 manifest 文件的信息,分析出哪些模块无需打包,直接从另外的文件暴露出来的内容中获取
    }),
  ],
}

在构建的时候,我们需要优先使用 webpack.dll.config.js 来打包,如 webpack --config webpack.dll.config.js --mode production,构建后生成公共代码模块的文件 vendor.jsmanifest.json,然后再进行应用代码的构建。

你会发现构建结果的应用代码中不包含 react 和 react-dom 的代码内容,这一部分代码内容会放在 vendor.js 这个文件中,而你的应用要正常使用的话,需要在 HTML 文件中按顺序引用这两个代码文件,如:

<script src="vendor.js"></script>
<script src="main.js"></script>

作用是不是和 optimization.splitChunks 很相似,但是有个区别,DLLPlugin 构建出来的内容无需每次都重新构建,后续应用代码部分变更时,你不用再执行配置为 webpack.dll.config.js 这一部分的构建,沿用原本的构建结果即可,所以相比 optimization.splitChunks,使用 DLLPlugin 时,构建速度是会有显著提高的。

但是很显然,DLLPlugin 的配置要麻烦得多,并且需要关心你公共部分代码的变化,当你升级 lodash(即你的公共部分代码的内容变更)时,要重新去执行 webpack.dll.config.js 这一部分的构建,不然沿用的依旧是旧的构建结果,使用上并不如 optimization.splitChunks 来得方便。这是一种取舍,根据项目的实际情况采用合适的做法。

还有一点需要注意的是,html-webpack-plugin 并不会自动处理 DLLPlugin 分离出来的那个公共代码文件,我们需要自己处理这一部分的内容,可以考虑使用 add-asset-html-webpack-plugin,关于这一个的使用就不讲解了,详细参考官方的说明文档:使用 add-asset-html-webpack-plugin。

流程优化

拆分构建步骤

这里拿图片的压缩作为一个例子讲解,我们在前边的小节提到图片可以使用 webpack 的 image-webpack-loader 来压缩图片,在对 webpack 构建性能要求不高的时候,这样是一种很简便的处理方式,但是要考虑提高 webpack 构建速度时,这一块的处理就得重新考虑一下了,思考一下是否有必要在 webpack 每次构建时都处理一次图片压缩。

我们可以直接使用 imagemin 来做图片压缩,编写简单的命令即可。然后使用 pre-commit 这个类库来配置对应的命令,使其在 git commit 的时候触发,并且将要提交的文件替换为压缩后的文件。

这样提交到代码仓库的图片就已经是压缩好的了,以后在项目中再次使用到的这些图片就无需再进行压缩处理了,image-webpack-loader 也就没有必要了。

使用同样的思路去考虑其他构建步骤,是否有必要在每次构建时处理,可以考虑用 git hooks 或者工作流的一些机制来触发一些构建工作,来减少 webpack 的构建压力。

拆分项目代码

webpack 的构建性能优化是比较琐碎的工作,当我们需要去考虑 webpack 的构建性能问题时,往往面对的是项目过大,涉及的代码模块过多的情况。在这种场景下你单独做某一个点的优化其实很难看出效果,你可能需要从我们上述提到的多个方面入手,逐一处理,验证,有些时候你甚至会觉得吃力不讨好,投入产出比太低了,这个时候我们可以考虑换一个角度来思考我们遇到的问题。

例如,拆分项目的代码,根据一定的粒度,把不同的业务代码拆分到不同的代码库去维护和管理,这样子单一业务下的代码变更就无须整个项目跟着去做构建,这样也是解决因项目过大导致的构建速度慢的一种思路,并且如果处理妥当,从工程角度上可能会给你带来其他的一些好处,例如发布异常时的局部代码回滚相对方便等等。

版本更新

官方发布的 webpack 4.0 更新日志来看,webpack 4.0 版本做了很多关于提升构建性能的工作,我觉得比较重要的改进有这么几个:

  • AST 可以直接从 loader 直接传递给 webpack,避免额外的解析,对这一个优化细节有兴趣的可以查看这个 PR。
  • 使用速度更快的 md4 作为默认的 hash 方法,对于大型项目来说,文件一多,需要 hash 处理的内容就多,webpack 的 hash 处理优化对整体的构建速度提升应该还是有一定的效果的。
  • Node 语言层面的优化,如用 for of 替换 forEach,用 MapSet 替换普通的对象字面量等等,这一部分就不展开讲了,有兴趣的同学可以去 webpack 的 PRs 寻找更多的内容。
  • 后续更新的版本把 uglifyjs-webpack-plugin 换成了 terser-webpack-plugin,可以更好地针对 ES6 的代码进行处理。

更多关于性能优化的 webpack 迭代可以查看官方的 release 文档:webpack release,搜索 performance 关键字就好。

webpack 5 的大版本,针对性能优化还有更棒的持久化缓存方案推出,让每一次的构建打包可以向我们的 webpack-dev-server 一样,只针对变化部分做增量构建,可以极大地提高构建性能,这个值得期待一下,有兴趣的同学可以使用 webpack 5 beta 版本来测试一下。

总结

本文我们首先介绍了如何从多个配置优化的方式来提高 webpack 的构建速度:

  • 减少 resolve 的解析
  • 把 loader 应用的文件范围缩小
  • 减少 plugin 的消耗
  • 选择合适的 devtool

在必要的时候,我们可以使用 thread-loader 和 DLLPlugin 来帮助我们进一步优化 webpack 的构建性能,但是从另外的角度考虑,在不过分依赖 webpack 构建的情况下,我们可以从流程优化上着手,如提交代码前就压缩图片,拆分构建的代码库等,以此来减少 webpack 构建的工作量。

同时,更新 Node 版本和 webpack 版本都有助于让我们的构建变得更快,勤劳的程序员们一直在优化代码库的性能,别辜负了他们的努力,尽可能让构建跑在最新的运行环境上吧。

你工作中遇到的项目是否有哪些部分是可以考虑使用上述的优化方法来提升构建速度的?你是否有其他的一些构建优化的方法呢?

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

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

相关文章

MYSQL索引+常见的锁

一)联合索引: 1)定义:是给一张表上面的多个列增加索引&#xff0c;也就是说给表上面的多个列增加索引&#xff0c;供快速查询使用&#xff0c;当两个列的组合是唯一值时&#xff0c;联合索引是个不错的选择 联合索引和单个索引对比来讲&#xff0c;联合索引的所有索引项都会出现…

左值右值笔记

左值右值 左值 左值是表示数据的表达式&#xff08;如变量名或解引用的指针&#xff09; 特点&#xff1a;可以获取地址&#xff0c;可以对他赋值。 位置&#xff1a;左值可以出现在赋值符号左边&#xff0c;也可以出现在赋值符号右边 右值 右值有:字面常量, 表达式返回值 …

用excel计算行列式的值

例如&#xff0c;我们要计算下面这个3*3矩阵的行列式的值&#xff1a; 127348569 鼠标点到其它空白的地方&#xff0c;用来存放计算后的结果&#xff1a; 插入-》函数&#xff1a; 选择MDETERM函数&#xff0c;这个就是计算行列式的函数&#xff1a; 点击“继续”&#xff1a…

函数极限求解方法归纳

1、连续函数直接代入值&#xff08;加减不可以部分代入值&#xff09; 例题1 配凑构造等价无穷小 等价无穷小 注意&#xff1a;不要在加减中部分使用等价无穷小&#xff0c;可以利用拆极限的方式求&#xff0c;拆出来的每一部分都要有极限&#xff0c;如果有一部分没有极限就是…

用excel计算矩阵的乘积

例如&#xff0c;我们要计算两个矩阵的乘积&#xff0c; 第一个矩阵是2*2的&#xff1a; 1234 第2个矩阵是2*3的&#xff1a; 5697810 在excel中鼠标点到其它空白的地方&#xff0c;用来存放矩阵相乘的结果&#xff1a; 选择插入-》函数&#xff1a; 选中MMULT&#xff0c;…

企业级信息化系统 ERP、OA、CRM、EAM、WMS、MES、PM

微服务架构&#xff0c;前端采用微应用架构&#xff0c;可做到不同服务使用不同数据库独立运行。全平台采用基于模型驱动的设计模式&#xff0c;并在前后端留有大量的代码植入入口&#xff0c;方便开发者对平台进行改造扩充。企业信息中心开发ERP、OA、CRM、EAM、WMS、MES、PM等…

PHP生成pdf格式准考证带照片完整示范

PDF效果图 PHP生成pdf格式准考证带照片完整示范以某省公务员考试下载的准考证模板为模板参考&#xff0c;故很有参考意义。 环境支持:linux PHP(5.5-7.3)环境,推荐宝塔环境。 基于fpdf.php插件开发&#xff0c;现有模板适合准考证生成并用于查询下载。 现有排版简单:替换data文…

MATLAB中Arrow 属性说明

目录 颜色和样式 位置 Arrow 属性是箭头的外观和行为。 Arrow 属性控制 Arrow 对象的外观和行为。通过更改属性值&#xff0c;可以修改箭头的特定方面。使用圆点表示法查询和设置属性。 ar annotation("arrow"); c ar.Color; ar.Color "red"; 颜色和…

LeetCode(7)买卖股票的最佳时机【数组/字符串】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 121. 买卖股票的最佳时机 1.题目 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票…

PHP开源自动化平台CRUD代码生成器

生成CRUD&#xff08;创建、读取、更新、删除&#xff09;代码的实现方式有很多种&#xff0c; 一、实现方式 1. 定义数据模型&#xff1a;首先需要定义数据模型&#xff0c;包括表结构、字段以及数据类型等。 2. 自动生成数据库表&#xff1a;根据数据模型&#xff0c;使用数…

Java编程--单例模式(饿汉模式/懒汉模式)/阻塞队列

前言 逆水行舟&#xff0c;不进则退&#xff01;&#xff01;&#xff01; 目录 单例模式 饿汉模式&#xff1a; 懒汉模式&#xff1a; 什么是阻塞队列 什么是高内聚 低耦合 阻塞队列的实现 单例模式 单例模式&#xff08;Singleton Pattern&#xff09;是一种常见…

vue,react虚拟dom

Virtual DOM 前言 在传统的Web开发中&#xff0c;直接操作真实的DOM通常是一个昂贵且低效的操作。为了解决这个问题&#xff0c;Virtual DOM&#xff08;虚拟DOM&#xff09;被引入为一个中间层&#xff0c;允许开发者在内存中进行操作&#xff0c;从而避免频繁且不必要的真实D…

【LeetCode刷题-二分查找】--69.x的平方根

69.x的平方根 class Solution {public int mySqrt(int x) {int l 0,r x ,ans -1;while(l < r){int mid (r - l)/2 l;if((long)mid * mid <x){ans mid;l mid 1;}else{r mid - 1;}}return ans;} }

(SpringBoot)第五章:SpringBoot创建和使用

文章目录 一&#xff1a;Spring和SpringBoot&#xff08;1&#xff09;Spring已解决和未解决的问题&#xff08;2&#xff09;SpringBoot 二&#xff1a;Spring项目的创建&#xff08;1&#xff09;IDEA创建&#xff08;2&#xff09;网页端创建 三&#xff1a;项目目录介绍及运…

在CMake中打印日志信息

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...) (无) &#xff1a;重要消息 STATUS &#xff1a;非重要消息 WARNING&#xff1a;CMake 警告, 会继续执行 AUTHOR_WARNING&#xff1a;CMake 警告 (dev), 会继续执行 SEN…

【左程云算法全讲7】二叉树基础

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于左程云算法课程进行的&#xff0c;每个知识点的修正和深入主要参考…

Python之函数进阶-闭包原理

Python之函数进阶-闭包原理 闭包 自由变量&#xff1a;未在本地作用域中定义的变量&#xff0c;例如定义在内层函数外的外层函数的作用域中的变量闭包&#xff1a;就是一个概念&#xff0c;出现在嵌套函数中&#xff0c;指的是内层函数引用到了外层函数的自由变量&#xff0c…

降维·预测·救命:PCA、随机森林与乳腺癌

一、引言 乳腺癌作为女性健康领域的一大挑战&#xff0c;对全球范围内的女性健康产生了深远影响。据世界卫生组织&#xff08;WHO&#xff09;统计&#xff0c;乳腺癌已成为全球女性恶性肿瘤发病率的最高者&#xff0c;且呈现逐年上升的趋势。在中国&#xff0c;乳腺癌也是女性…

【LeetCode刷题笔记】滑动窗口

992. K 个不同整数的子数组 解题思路: 滑动窗口 , 题目问题转化为: 求 「最多存在 K 个不同整数的子数组的个数」 与 「最多存在 K - 1 个不同整数的子数组的个数」 之差, 就是题目所求的 「恰好存在 K 个不同整数的子数组的个数」 , 最终问题就变成求解滑动窗口内,以 R …

Mac媒体播放器 Fig Player 1.3.7

Fig Player是一款功能强大的多媒体播放器软件&#xff0c;适用于macOS操作系统。它支持广泛的音频和视频格式&#xff0c;如MP3、MP4、AVI、MKV等&#xff0c;并提供了直观易用的用户界面&#xff0c;使用户可以轻松浏览和管理媒体库。 Fig Player具有快速加载和播放媒体文件的…