携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
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并行构建。