js
是一门单线程编程语言,无法完全发挥多核cpu
的所有性能,那么如何在webpack
中并行构建资源,完整的利用cpu
的性能呢?这里带来了4个技术方案,就是下面这些,接下来就看他们有什么特性,和如何使用的吧。
- HappyPack:多进程方式运行资源加载(Loader)逻辑;
- Thread-loader:Webpack 官方出品,同样以多进程方式运行资源加载逻辑;
- Parallel-Webpack:多进程方式运行多个 Webpack 构建实例;
- TerserWebpackPlugin:支持多进程方式执行代码压缩、uglify 功能。
HappyPack
HappyPack
是将文件加载(loader
)进行拆分,让每一种类型的文件单独新建一个子进程中解析,让多种类型的文件并行执行,执行完成之后再将结果返回给webpack
,使用方法如下:
- 安装依赖:
npm i -D happypack
- 使用
happypack
使用happypack
需要将原有的配置进行改造,happypack
本身是插件使用方式,需要将loader
配置传入到happypack
的插件实例化作为初始参数,原本的loader
需要替换成happypack
提供的loader
,如下:
// 原本的使用方式 const path = require("path"); module.exports = { entry: './main.js', output: { path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(png|jpg|bmp|svg)$/, type: "asset" }, { test: /\.js/, use: ['babel-loader'] }, { test: /\.css/, use: ['css-loader'] } ] } }
// 使用 happypack 的方式 const path = require("path"); const HappyPack = require('happypack'); module.exports = { entry: './main.js', output: { path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(png|jpg|bmp|svg)$/, type: "asset" }, { test: /\.js$/, use: 'happypack/loader?id=js' }, { test: /\.css/, use: 'happypack/loader?id=css' } ] }, plugins: [ new HappyPack({ id: 'js', use: ['babel-loader'] }), new HappyPack({ id: 'css', use: ['style-loader'] }) ] }
猛一看,配置变多了,看看就好了,我自己测试发现构建失败,然后上了github
看了一下,4年没更新了,作者也表示不再维护了,这里就权当了解一下历史。
他配置的plugins
就是相当于new
一个就创建一个新的子线程,然后里面的参数,就是处理对应文件的loader
就在这个线程中处理,多个文件就可以并发构建,增加构建效率。
Thread-loader
Thread-loader
是webpack
官方提供的多线程并行构建的工具,对比happypack
那肯定是更稳定,更便捷了,但是他们的功能都类似,直接来看如何使用:
- 安装
npm i -D thread-loader
- 配置
const path = require("path"); module.exports = { mode: "production", entry: './main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name].js" }, module: { rules: [ { test: /.(png|jpg|bmp|svg)$/, type: "asset" }, { test: /.js/, use: ['thread-loader', 'babel-loader'], // 直接将 thread-loader 加到最前面就可以了,在它后面的 loader 就会在一个单独的线程池中运行 }, { test: /.css/, use: ['style-loader', 'css-loader'], // 这个没加因为加上去就报错 } ] } }
需要注意的是使用thread-loader
也有限制,下面来自官方文档:
在 worker 池(worker pool)中运行的 loader 是受到限制的。例如:
- 这些 loader 不能产生新的文件。
- 这些 loader 不能使用定制的 loader API(也就是说,通过插件)。
- 这些 loader 无法获取 webpack 的选项设置。
每个 worker 都是一个单独的有 600ms 限制的 node.js 进程。同时跨进程的数据交换也会被限制。
请仅在耗时的 loader 上使用
文档中还有很多配置项可以使用,这里就不一一介绍了,介绍完了感觉有点像文档搬运工。
Parallel-Webpack
上面的两个loader
的作用域是在文件解析这一块,之前介绍流程中有讲过,文件解析后面还有文件生成,所以咧就有了一个这个玩意,不过这个玩意并不是把文件解析、文件生成啥的都搞一个线程去处理,而是对webpack
的配置对象,每一个配置对象开辟独立的线程处理,是可以和上面两个工具并存处理构建。
- 安装:
npm i -D parallel-webpack
- 不需要配置,直接使用,命令行:
npx parallel-webpack
。
当然上面这样直接使用肯定是看不到效果的,只是配置同普通的weppack
相同:
const path = require("path"); module.exports = [ { mode: "production", entry: './main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name]-amd.js", library: { type: "amd" } }, module: { rules: [ { test: /.(png|jpg|bmp|svg)$/, type: "asset" }, { test: /.js/, use: ['thread-loader', 'babel-loader'] }, { test: /.css/, use: ['style-loader', 'css-loader'] } ] } }, { mode: "production", entry: './main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name]-cmd.js", library: { type: 'commonjs' } }, module: { rules: [ { test: /.(png|jpg|bmp|svg)$/, type: "asset" }, { test: /.js/, use: ['thread-loader', 'babel-loader'] }, { test: /.css/, use: ['style-loader', 'css-loader'] } ] } } ]
上面是导出两个配置(就是把刚刚写的案例复制了一下),使用的libaray
属性输出不同的包,之前讲过如果直接使用webpack
进行构建,会因为js
语言的特性,构建时间会加倍,使用这种方式就可以并行构建,减少构建时间。
并行压缩
Webpack4 默认使用 Uglify-js 实现代码压缩,Webpack5 之后则升级为 Terser —— 一种性能与兼容性更好的 JavaScript 代码压缩混淆工具,两种组件都原生实现了多进程并行压缩能力。
- 安装:
npm i -D terser-webpack-plugin
- 配置
const TerserPlugin = require('terser-webpack-plugin') module.exports = { // 省略其他配置 optimization: { minimize: true, minimizer: [new TerserPlugin({ parallel: 2, // 这里的默认值为 true,数据类型: Boolean|Number })] } }
这个插件就是对文件进行压缩混淆的,当然这一块如果项目庞大,也是很耗时的,所以就有了一个并行压缩工具,加快构建速度。
总结
javascript
的特性注定无法完全发挥cpu
多核计算的能力,但是也不是完全限制死了,javascript
是单线程,那只是代表一个js
进程是单线程,开辟多个进程进行处理是否就是多线程呢?这是不是可以说明js
不是单线程呢?
上面是玩笑话,多线程数据可以互相访问,这种多进程数据是无法直接互通的,但是处理多个不相关的业务还是OK的,于是就有了这次的webpack
并行构建。