本篇介绍一些webpack优化的配置方法,目的有二:
“注意:在软件工程领域有一条十分重要的功能经验,不要过早优化。在项目初期不要看到一个可以优化的点就去做优化,这样极有可能会增加尤其开发及维护的复杂度,并且从整体效果看,优化效果不会太理想。
“HappyPack是一个通过多线程来提升webpack打包速度的工具
在打工过程中,非常耗时的一个工作是使用loader将各种资源进行转译处理,例如常见的使用babel-loader将ES6+语法代码转译为ES5等。代码转移的工作流程如下:
从上述步骤中可以看出,从步骤2)到步骤4)其实是一个递归的过程,webpack需要一步步地获取更下一级的资源然后逐个进行转译。为什么逐个?问题就在于webpack是单线程的。而HappyPack便将这里作为切入口,它的核心特性是可以开启多个线程,并行的对不同模块进行转译,这样便更加充分的利用计算机资源来提升打包速度。
HappyPack显然更加适用于转译任务比较繁重的工程,当我们把类似babel-loadre,ts-loader等迁移到HappyPack之上后,一般会有比较不错的效果,而对于sass-loader等本身消耗时间并不太多的功能则效果一般。
以babel-loader为例:
// 初始webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: ['react'],
}
}
]
}
}
使用HappyPack:
// 使用HappyPack:
const HappyPack = require('happypack');
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'happypack/loader'
}
]
},
plugin: [
new HappyPack({
loaders: [
{
loader: 'babel-loader',
options: {
presets: ['react']
}
}
]
})
]
}
在module.rules中,我们使用happypack/loader替换掉了原来的babel-loader,而在plugins中添加了HappyPack插件,将原来的bebel-loader及其配置插入进去即可。
在使用HappyPack对多个loader进行优化时,需要为每一个loader配置一个id,否则HappyPack无法知道rules与plugins的对应关系。
这里以babel-loader及ts-loader为例:
// webpack.config.js
const HappyPack = require('happypack');
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'happypack/loader?id=js'
},
{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'happypack/loader?id=ts'
}
]
},
plugin: [
new HappyPack({
id: 'js',
loaders: [
{
loader: 'babel-loader',
options: {
// babel配置
}
}
]
}),
new HappyPack({
id: 'ts',
loaders: [
{
loader: 'ts-loader',
options: {
// ts配置
}
}
]
})
]
}
除上述配置之外,可以查阅官方文档查看更多的配置,例如线程数,debug模式等。
从宏观角度看,提升性能的方式总结为两种:
而上面我们所了解的HappyPack明显属于增加资源,那接下来介绍从缩小范围的几个方案。
在前面章节(预处理器(loader)【上篇】)中,介绍过exclude和include,在配置loader的时候一般都会对其进行配置。对于JS来说,一般需要把node_modules目录排除掉,另外当exclude和include规则有重叠部分时,exclude优先级更高。
那对于此处,我们使用include让babel-loader只生效于源码目录:
// webpack.config.js
...
module: {
rules: [
test: /\.js$/,
include: /src\/scripts/,
loader: 'babel-loader'
]
}
有些库我们希望webpack完全不要去解析,那此时可以使用noParse对其进行忽略,如:
// webpack.config.js
// ...
module.exports = {
noParse: {
// fullPath是绝对路径,如 /User/me/app/webpack/noparse/lib/lodash.js
return /lib/.test(fullPath);
}
}
如上配置将忽略所有lib目录下的资源解析。
exclude和include是确定loader的规则范围,noParse是不去解析但仍会打包到bundle中,那接下来介绍一个插件——IgnorePlugin,他可以完全排除一些模块,被排除的模块即使被引用也不会被打包进资源文件中。一般作用于排除一些库相关文件。
一个由库产生的额外资源我们用不到但没办法去掉时,可以考虑使用此方法处理。
例,一个日期时间处理的相关插件Moment.js,为了做本地化会加载许多语言包,但一般我们只会用到本地的语言包而不会使用其他地区的语言包,而语言包会占用很大体积,这时可以使用IgnorePlugin来做处理:
// webpack.config.js
...
plugins: [
new webpack.IgnorePlugin({
resourceRegexp: /^\.\/locale$/, // 匹配资源文件
contextRegExp: /moment$/ // 匹配检索目录
})
]
我们在使用某些loader时会有一个cache的配置项,用来在编译代码后同时保存一份缓存,在执行下一次编译前会优先检查源码文件是否有变化,没有则直接使用缓存结果,也就是上次编译的结果。这样一来,只有在发生变化时编译变化了的文件,对于整体而言也属于一种优化处理。
本篇从多线程打包
和缩小打包作用域
两个方面入手,对webpack打包层面做出优化。下一篇描述从动态链接库思想
与死代码检测
方面继续深入探究打包层面的深度优化。