webpack 打包优化并没有什么固定的模式,一般我们常见的优化就是拆包、分块、压缩等,并不是对每一个项目都适用,针对于特定项目,需要不断调试不断优化。
对于 webpack4,建议从零开始配置,在项目初期,使用 webpack4 默认的配置。
接下来,本篇文章会列出所有适用于 webpack 优化打包速度的技术方案,并给出相应的限制,请在实际项目中分情况使用。如有任何疑问,请联系瓶子君。
优化 webpack 构建速度的第一步是知道将精力集中在哪里。我们可以通过 测量你的 webpack 构建期间各个阶段花费的时间:
特定的项目,都有自己特定的性能构建瓶颈,下面我们对打包的每一个环节进行优化。
在「窥探原理:手写一个 JavaScript 打包器」中,我们已经介绍过,打包就是从入口文件开始将所有的依赖模块打包到一个文件中的过程,当然,在打包过程中涉及各种编译、优化过程。
打包过程中,常见影响构建速度的地方有哪些喃?
搜索所有的依赖项,这需要占用一定的时间,即搜索时间,那么我们就确定了:
我们需要优化的第一个时间就是搜索时间。
webpack 根据我们配置的 loader 解析相应的文件。日常开发中我们需要使用 loader 对 js ,css ,图片,字体等文件做转换操作,并且转换的文件数据量也是非常大。由于 js 单线程的特性使得这些转换操作不能并发处理文件,而是需要一个个文件进行处理。
我们需要优化的第二个时间就是解析时间。
将所有解析完成的代码,打包到一个文件中,为了使浏览器加载的包更新(减小白屏时间),所以 webpack 会对代码进行优化。
JS 压缩是发布编译的最后阶段,通常 webpack 需要卡好一会,这是因为压缩 JS 需要先将代码解析成 AST 语法树,然后需要根据复杂的规则去分析和处理 AST,最后将 AST 还原成 JS,这个过程涉及到大量计算,因此比较耗时,打包就容易卡住。
我们需要优化的第三个时间就是压缩时间。
当更改项目中一个小小的文件时,我们需要重新打包,所有的文件都必须要重新打包,需要花费同初次打包相同的时间,但项目中大部分文件都没有变更,尤其是第三方库。
我们需要优化的第四个时间就是二次打包时间。
运行在 Node.js 之上的 webpack 是单线程模式的,也就是说,webpack 打包只能逐个文件处理,当 webpack 需要打包大量文件时,打包时间就会比较漫长。
把这个 loader 放置在其他 loader 之前, 放置在这个 loader 之后的 loader 就会在一个单独的 worker【worker pool】 池里运行,一个worker 就是一个nodeJS 进程【node.js proces】,每个单独进程处理时间上限为600ms,各个进程的数据交换也会限制在这个时间内。
thread-loader 使用起来也非常简单,只要把 thread-loader 放置在其他 loader 之前, 那 thread-loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行。
例如:
注意:thread-loader 放在了 style-loader 之后,这是因为 thread-loader 后的 loader 没法存取文件也没法获取 webpack 的选项设置。
官方上说每个 worker 大概都要花费 600ms ,所以官方为了防止启动 worker 时的高延迟,提供了对 worker 池的优化:预热
注意:请仅在耗时的 loader 上使用。
在webpack构建过程中,实际上耗费时间大多数用在 loader 解析转换以及代码的压缩中,HappyPack 可利用多进程对文件进行打包(默认cpu核数-1),对多核cpu利用率更高。HappyPack 可以让 Webpack 同一时间处理多个任务,发挥多核 CPU 的能力,将任务分解给多个子进程去并发的执行,子进程处理完后,再把结果发送给主进程。
happypack 的处理思路是将原有的 webpack 对 loader 的执行过程从单一进程的形式扩展多进程模式,原本的流程保持不变。使用 HappyPack 也有一些限制,它只兼容部分主流的 loader,具体可以查看官方给出的 兼容性列表。
注意:Ahmad Amireh 推荐使用 thread-loader,并宣布将不再继续维护 happypack,所以不推荐使用它
注意,当项目较小时,多进程打包反而会使打包速度变慢。
使用 webpack 缓存的方法有几种,例如使用 , 或 的 标志。 所有这些缓存方法都有启动的开销。 重新运行期间在本地节省的时间很大,但是初始(冷)运行实际上会更慢。
如果你的项目生产版本每次都必须进行初始构建的话,缓存会增加构建时间,减慢你的速度。如果不是,那它们就会大大缩减你二次构建的时间。
cache-loader 和 thread-loader 一样,使用起来也很简单,仅仅需要在一些性能开销较大的 loader 之前添加此 loader,以将结果缓存到磁盘里,显著提升二次构建速度。
请注意,保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此 loader。
webpack3 启动打包时加上 ,这样 Webpack 会自动为你注入一个带有默认配置的 UglifyJSPlugin 。
或:
压缩 JavaScript 代码需要先把代码解析成用 Object 抽象表示的 AST 语法树,再去应用各种规则分析和处理 AST,导致这个过程计算量巨大,耗时非常多。但 是单线程,所以我们可以使用 。
插件实现了多进程压缩, 会开启多个子进程,把对多个文件的压缩工作分配给多个子进程去完成,每个子进程其实还是通过 去压缩代码,但是变成了并行执行。 所以 能更快的完成对多个文件的压缩工作。
webpack4 中 已被废弃。
也不推荐使用 ParallelUglifyPlugin,项目基本处于没人维护的阶段,issue 没人处理,pr没人合并。
webpack4 默认内置使用 插件压缩优化代码,而该插件使用 来缩小 。
所谓 terser,官方给出的定义是:
用于 ES6+ 的 JavaScript 解析器、mangler/compressor(压缩器)工具包。
为什么 webpack 选择 terser?
不再维护 uglify-es ,并且 uglify-js 不支持 ES6 +。
terser 是 uglify-es 的一个分支,主要保留了与 uglify-es 和 uglify-js@3 的 API 和 CLI 兼容性。
使用多进程并行运行来提高构建速度。并发运行的默认数量为 。
可以显著加快构建速度,因此强烈推荐开启多进程
webpack 打包时,会从配置的 触发,解析入口文件的导入语句,再递归的解析,在遇到导入语句时 webpack 会做两件事情:
以上两件事情虽然对于处理一个文件非常快,但是当项目大了以后文件量会变的非常多,这时候构建速度慢的问题就会暴露出来。 虽然以上两件事情无法避免,但需要尽量减少以上两件事情的发生,以提高速度。
接下来一一介绍可以优化它们的途径。
使用 Loader 时可以通过、、三个配置项来命中 Loader 要应用规则的文件
?用于配置 webpack 去哪些目录下寻找第三方模块,?的默认值是? ,含义是先去当前目录下的目录下去找想找的模块,如果没找到就去上一级目录中找,再没有就去中找,以此类推。
?配置项通过别名来把原导入路径映射成一个新的导入路径,减少耗时的递归解析操作。
在导入语句没带文件后缀时,webpack 会根据 resolve.extension 自动带上后缀后去尝试询问文件是否存在,所以在配置? 应尽可能注意以下几点:
有一些第三方模块会针对不同环境提供几分代码。 例如分别提供采用 ES5 和 ES6 的2份代码,这2份代码的位置写在 文件里,如下:
webpack 会根据 的配置去决定优先采用那份代码, 默认如下:
webpack 会按照数组里的顺序去 文件里寻找,只会使用找到的第一个。
假如你想优先采用 ES6 的那份代码,可以这样配置:
?配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能。 原因是一些库,例如 jQuery 、ChartJS, 它们庞大又没有采用模块化标准,让 Webpack 去解析这些文件耗时又没有意义。
以上就是所有和缩小文件搜索范围相关的构建性能优化了,在根据自己项目的需要去按照以上方法改造后,你的构建速度一定会有所提升。
是时候抛弃Postman了,试试 VS Code 自带神器插件
如果觉得不错,就点个赞吧!
想看往期更过系列文章,点击前往 github 博客主页
玩得开心,不断学习,并始终保持编码。
如有任何问题或更独特的见解,欢迎评论或直接联系瓶子君(扫码关注公众号回复 123 即可)!
欢迎关注:前端瓶子君,每日更新!