(点击
上方公众号
,可快速关注)
作者:晚晴幽草轩
www.jeffjade.com/2017/08/12/125-webpack-package-optimization-for-speed/
如有好文章投稿,请点击 → 这里了解详情
在前文
Webpack 打包优化之体积篇
中,对如何减小 Webpack 打包体积,做了些探讨;当然,那些法子对于打包速度的提升,也是大有裨益。然而,打包速度之于开发体验和及时构建,相当重要;所以有必要对其做更为深入的研究,以便完善工作流,这就是本文存在的缘由。
减小文件搜索范围
在使用实际项目开发中,为了提升开发效率,很明显你会使用很多成熟第三方库;即便自己写的代码,模块间相互引用,为了方便也会使用相对路劲,或者别名(alias);这中间如果能使得 Webpack 更快寻找到目标,将对打包速度产生很是积极的影响。于此,我们需要做的即:减小文件搜索范围,从而提升速度;实现这一点,可以有如下两法:
配置 resolve.modules
Webpack的resolve.modules配置模块库(即 node_modules)所在的位置,在 js 里出现 import 'vue' 这样不是相对、也不是绝对路径的写法时,会去 node_modules 目录下找。但是默认的配置,会采用向上递归搜索的方式去寻找,但通常项目目录里只有一个 node_modules,且是在项目根目录,为了减少搜索范围,可以直接写明 node_modules 的全路径;同样,对于别名(alias)的配置,亦当如此:
function
resolve
(
dir
)
{
return
path
.
join
(
__dirname
,
'..'
,
dir
)
}
module
.
exports
=
{
resolve
:
{
extensions
:
[
'.js'
,
'.vue'
,
'.json'
],
modules
:
[
resolve
(
'src'
),
resolve
(
'node_modules'
)
],
alias
:
{
'vue$'
:
'vue/dist/vue.common.js'
,
'src'
:
resolve
(
'src'
),
'assets'
:
resolve
(
'src/assets'
),
'components'
:
resolve
(
'src/components'
),
// ...
'store'
:
resolve
(
'src/store'
)
}
},
...
}
需要额外补充一点的是,这是 Webpack2.* 以上的写法。在 1.* 版本中,使用的是 resolve.root,如今已经被弃用为 resolve.modules;同时被弃用的还有resolve.fallback、resolve.modulesDirectories。
设置 test & include & exclude
Webpack 的装载机(loaders),允许每个子项都可以有以下属性:
test:必须满足的条件(正则表达式,不要加引号,匹配要处理的文件)
exclude:不能满足的条件(排除不处理的目录)
include:导入的文件将由加载程序转换的路径或文件数组(把要处理的目录包括进来)
loader:一串“!”分隔的装载机(2.0版本以上,”-loader”不可以省略)
loaders:作为字符串的装载器阵列
对于include,更精确指定要处理的目录,这可以减少不必要的遍历,从而减少性能损失。同样,对于已经明确知道的,不需要处理的目录,则应该予以排除,从而进一步提升性能。假设你有一个第三方组件的引用,它肯定位于node_modules,通常它将有一个 src 和一个 dist 目录。如果配置 Webpack 来排除 node_modules,那么它将从 dist 已经编译的目录中获取文件。否则会再次编译它们。故而,合理的设置 include & exclude,将会极大地提升 Webpack 打包优化速度,比如像这样:
module
:
{
preLoaders
:
[
{
test
: /
\.
js
$
/
,
loader
:
'eslint'
,
include
:
[
resolve
(
'src'
)],
exclude
: /
node_modules
/
},
{
test
: /
\.
svg
$
/
,
loader
:
'svgo?'
+
JSON
.
stringify
(
svgoConfig
)
,
include
:
[
resolve
(
'src/assets/icons'
)],
exclude
: /
node_modules
/
}
],
loaders
:
[
{
test
: /
\.
vue
$
/
,
loader
:
'vue-loader'
,
include
:
[
resolve
(
'src'
)],
exclude
: /
node_modules
\
/
(?
!
(
autotrack
|
dom
-
utils
))
|
vendor
\.
dll
\.
js
/
},
{
test
: /
\.(
png
|
jpe
?
g
|
gif
|
svg
)(\?.
*
)?$
/
,
loader
:
'url'
,
exclude
:
/assets\/i
cons
/
,
query
:
{
limit
:
10000
,
name
:
utils
.
assetsPath
(
'img/[name].[hash:7].[ext]'
)
}
}
]
}
增强代码代码压缩工具
Webpack 默认提供的 UglifyJS 插件,由于采用单线程压缩,速度颇慢 ;推荐采用 webpack-parallel-uglify-plugin 插件,她可以并行运行 UglifyJS 插件,更加充分而合理的使用 CPU 资源,这可以大大减少的构建时间;当然,该插件应用于生产环境而非开发环境,其做法如下,
new
webpack
.
optimize
.
UglifyJsPlugin
({
compress
:
{
warnings
:
false
},
sourceMap
:
true
})
替换如上自带的 UglifyJsPlugin 写法为如下配置即可:
var
ParallelUglifyPlugin
=
require
(
'webpack-parallel-uglify-plugin'
);
new
ParallelUglifyPlugin
({
cacheDir
:
'.cache/'
,
uglifyJS
:
{
output
:
{
comments
:
false
},
compress
:
{
warnings
:
false
}
}
})
当然也有其他同类型的插件,比如:webpack-uglify-parallel,但根据自己实践效果来看,并没有 webpack-parallel-uglify-plugin 表现的那么卓越,有兴趣的朋友,可以更全面的做下对比,择优选用。需要额外说明的是,webpack-parallel-uglify-plugin 插件的运用,会相对 UglifyJsPlugin 打出的包,看起来略大那么一丢丢(其实可以忽略不计);如果在你使用时也是如此,那么在打包速度跟包体积之间,你应该有自己的抉择。
用 Happypack 来加速代码构建
你知道,Webpack 中为了方便各种资源和类型的加载,设计了以 loader 加载器的形式读取资源,但是受限于 nodejs 的编程模型影响,所有的 loader 虽然以 async 的形式来并发调用,但是还是运行在单个 node 的进程,以及在同一个事件循环中,这就直接导致了些问题:当同时读取多个loader文件资源时,比如`babel-loader`需要 transform 各种jsx,es6的资源文件。在这种同步计算同时需要大量耗费 cpu 运算的过程中,node的单进程模型就无优势了,而 Happypack 就是针对解决此类问题而生的存在。
Happypack 的处理思路是:将原有的 webpack 对 loader 的执行过程,从单一进程的形式扩展多进程模式,从而加速代码构建;原本的流程保持不变,这样可以在不修改原有配置的基础上,来完成对编译过程的优化,具体配置如下:
var
HappyPack
=
require
(
'happypack'
);
var
happyThreadPool
=
HappyPack
.
ThreadPool
({
size
:
os
.
cpus
().
length
});
module
:
{
loaders
:
[
{
test
: /
\.
js
[
x
]?$
/
,
include
:
[
resolve
(
'src'
)],
exclude
: /
node_modules
/
,
loader
:
'happypack/loader?id=happybabel'
}
]
},
plugins
:
[
new
HappyPack
({
id
:
'happybabel'
,
loaders
:
[
'babel-loader'
],
threadPool