主题
构建性能--并行
- HappyPack:多进程方式运行资源加载(Loader)逻辑;
- Thread-loader:Webpack 官方出品,同样以多进程方式运行资源加载逻辑;
- Parallel-Webpack:多进程方式运行多个 Webpack 构建实例;
- TerserWebpackPlugin:支持多进程方式执行代码压缩、uglify 功能。
这些方案的核心设计都很类似:针对某种计算任务创建子进程,之后将运行所需参数通过 IPC 传递到子进程并启动计算操作,计算完毕后子进程再将结果通过 IPC 传递回主进程,寄宿在主进程的组件实例,再将结果提交给 Webpack。
HappyPack
HappyPack 已经不再维护,扩展性与稳定性缺乏保障,比较适合在 Webpack4 及以下版本使用
- 安装依赖
shell
npm i -D happypack
- 将原有 loader配置替换成
happypack/loader
,并将原有 loader 配置迁移到插件中
js
const HappyPack = require('happypack');
module.exports = {
// ...
module: {
rules: [{
test: /\.js?$/,
// 使用 `id` 参数标识该 Loader 对应的 HappyPack 插件示例
use: 'happypack/loader?id=js'
},
{
test: /\.less$/,
use: 'happypack/loader?id=styles'
},
]
},
plugins: [
new HappyPack({
// 注意这里要明确提供 id 属性
id: 'js',
loaders: ['babel-loader', 'eslint-loader']
}),
new HappyPack({
id: 'styles',
loaders: ['style-loader', 'css-loader', 'less-loader']
})
]
};
默认情况下,HappyPack 插件实例 自行管理 自身所消费的进程,需要导致频繁创建、销毁进程实例 —— 这是非常昂贵的操作,反而会带来新的性能损耗,所以可以使用 HappyPack 提供的共享进程池接口
js
const os = require('os')
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({
// 设置进程池大小
size: os.cpus().length - 1
});
module.exports = {
// ...
plugins: [
new HappyPack({
id: 'js',
// 设置共享进程池
threadPool: happyThreadPool,
loaders: ['babel-loader', 'eslint-loader']
}),
new HappyPack({
id: 'styles',
threadPool: happyThreadPool,
loaders: ['style-loader', 'css-loader', 'less-loader']
})
]
};
使用 HappyPack.ThreadPool
接口后,HappyPack 会预先创建好一组工作进程,所有插件实例的资源转译任务会通过内置的 HappyThread
对象转发到空闲进程做处理,避免频繁创建、销毁进程。
Thread-loader
Thread-loader 由 Webpack 官方提供,目前还处于持续迭代维护状态,理论上更可靠,且使用方式更简单方便
- 安装依赖
shell
npm i -D thread-loader
将 Thread-loader 放在
use
数组首位,确保最先运行style-loader 等不能调用
emitAsset
的 loader 需要放在 thread-loader 前
js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ["thread-loader", "babel-loader", "eslint-loader"],
},
],
},
};
Thread-loader 还提供了一系列用于控制并发逻辑的配置项,包括:
workers
:子进程总数,默认值为require('os').cpus() - 1
;workerParallelJobs
:单个进程中并发执行的任务数;poolTimeout
:子进程如果一直保持空闲状态,超过这个时间后会被关闭;poolRespawn
:是否允许在子进程关闭后重新创建新的子进程,一般设置为false
即可;workerNodeArgs
:用于设置启动子进程时,额外附加的参数。
js
{
loader: "thread-loader",
options: {
workers: 2
}
}
Thread-loader 也同样面临着频繁的子进程创建、销毁所带来的性能问题,为此,Thread-loader 提供了 warmup
接口用于前置创建若干工作子进程,降低构建时延
js
const threadLoader = require("thread-loader");
threadLoader.warmup(
{
// 可传入上述 thread-loader 参数
workers: 2,
workerParallelJobs: 50,
},
[
// 子进程中需要预加载的 node 模块
"babel-loader",
"babel-preset-es2015",
"sass-loader",
]
);
Parallel-webpack
- 安装依赖
shell
npm i -D parallel-webpack
- 配置多个 webpack 对象
js
module.exports = [{
entry: 'pageA.js',
output: {
path: './dist',
filename: 'pageA.js'
}
}, {
entry: 'pageB.js',
output: {
path: './dist',
filename: 'pageB.js'
}
}];
- 执行
npx parallel-webpack