在webpack 4
时使用的happyPack
,因为项目需要升级依赖,再升级babel-core
依赖至@babel/core
,编译运行项目时,一直报错
cannot found babel-core module
定位问题存在在于调用了babel-core
相关的webpack,找到了happyPack
配置。
1、浅谈happyPack
happyPack实现多线程编译
webpack在node中是单线程的,但是在使用webpack编译项目时,loader、plugin需要处理的文件数量很大,webpack单线程处理导致效率低、速度慢,可以使用happyPack来实现多线程工作。happyPack
将不同的loader、plugin工作分配到不同的子线程,在子线程结束完后再推入到主线程中,多线程同时编译同时减少编译时间。
基本使用
HappyPack同时提供plugin
和loader
以完成其工作,因此您必须同时使用两者
来启用它。
const path = require('path');
const HappyPack = require('happypack');
module.exports = {
module: {
rules: [
{
test: /.js$/,
// 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
use: ['happypack/loader?id=babel'],
// 排除 node_modules 目录下的文件,node_modules 目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换
exclude: path.resolve(__dirname, 'node_modules'),
},
{
// 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例
test: /.css$/,
use: ExtractTextPlugin.extract({
use: ['happypack/loader?id=css'],
}),
},
]
},
plugins: [
new HappyPack({
// 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
id: 'babel',
// 如何处理 .js 文件,用法和 Loader 配置中一样
loaders: ['babel-loader?cacheDirectory'],
// ... 其它配置项
}),
new HappyPack({
id: 'css',
// 如何处理 .css 文件,用法和 Loader 配置中一样
loaders: ['css-loader'],
}),
new ExtractTextPlugin({
filename: `[name].css`,
}),
],
};
loader
配置中,全部交给happyPck/loader
处理,其后的id=babel
告诉happyPck/loader
选择哪个happyPack实例处理文件plugin
中,options
中设置的id
和loader
中使用的id
对应。
实例化 happyPack 时的其他options
-
threads
代表开启几个子进程去处理这一类型的文件,默认是3个,类型必须是整数。 -
verbose
是否允许HappyPack输出日志,默认是true
。 -
threadPool
代表共享进程池,即多个HappyPack实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多,相关代码如下:
const HappyPack = require('happypack')
const os = require('os')
//os nodejs os模块提供了一些基本的系统操作函数
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
module.exports = {
...
plugins: [
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
loaders: ['babel-loader?cacheDirectory=true']
}),
]
}
基本原理
在整个 Webpack 构建流程中,最耗时的流程可能就是
Loader
对文件的转换操作了,因为要转换的文件数据巨多,而且这些转换操作都只能一个个挨着处理。 HappyPack的核心原理就是把这部分任务分解到多个进程去并行处理,从而减少了总的构建时间。
从前面的使用中可以看出所有需要通过Loader
处理的文件都先交给了happypack/loader
去处理,收集到了这些文件的处理权后HappyPack就好统一分配了。
每通过new HappyPack()
实例化一个HappyPack其实就是告诉HappyPack核心调度器如何通过一系列Loader
去转换一类文件,并且可以指定如何给这类转换操作分配子进程。
核心调度器的逻辑代码在主进程中,也就是运行着Webpack的进程中,核心调度器会把一个个任务分配给当前空闲的子进程,子进程处理完毕后把结果发送给核心调度器,它们之间的数据交换是通过进程间通信API实现的。
核心调度器收到来自子进程处理完毕的结果后会通知Webpack该文件处理完毕
2、thread-load替代happyPack
因为happyPack
作者目前很少从事JavaScript工作,所以不维护了,推荐使用thread-loader。
使用thread-load
可以实现将不同编译工作放入不同的工作池中运行。
简述
将thread-load
放在其他loader
的前面即可,那么在它其后的loader
就会单独运行在一个工作池中。
在工作池中运行的loader
是有限制的:
- Loaders 不能产生新的文件
- Loaders 不能使用自定义的loader API(也就是说,通过插件)
- Loaders 无法获取 webpack 的选项设置。
每个worker都是一个单独的node.js进程,其开销约为600ms。同时跨进程的数据交换也会被限制。
[thread-loader]中建议:请仅在耗时的 loader 上使用
使用方法
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve('src'),
use: [
'thread-loader',
// your expensive loader (e.g babel-loader)
],
},
],
},
};
选项options配置:
use: [
{
loader: 'thread-loader',
// 具有相同options的loader将共享工作池
options: {
// worker的数量,默认为(cpus的数量- 1)或
// 当require('os').cpus()未定义时,返回1
workers: 2,
// 一个worker并发处理的工作数,默认为20
workerParallelJobs: 50,
// 额外的node . js参数
workerNodeArgs: ['--max-old-space-size=1024'],
// 允许复活一个死亡的工作池\
// 重生减慢整个编译\
// 和应该设置为false的开发
poolRespawn: false,
// 闲置时定时删除 worker 进程
// 默认为500(毫秒)
// 以设置为无穷大, 这样在监视模式(--watch)下可以保持 worker 持续存在
poolTimeout: 2000,
// 工作池分配给worker的工作数量。默认为200.
// 降低这个数值会降低总体的效率,但是会提升工作分布更均一
poolParallelJobs: 50,
// 工作池名称。可以修改名称来创建其余选项都一样的池(pool)
name: 'my-pool',
},
},
// 其他你需要的loader,例如 babel-loader
];