基于vue-cli的webpack优化之路

最近的项目度过了开始忙碌的基建期,也慢慢轻松下来,准备记录一下自己最近webpack优化的措施,希望有温故知新的效果。

项目采用的是vue全家桶,构建配置都是基于vue-cli去改进的。关于原始webpack配置大家可以看下这篇文章vue-cli#2.0 webpack配置分析,文章基本对于文件每行代码都做了详细的解释,有助于更好的理解webpack。
项目位置链接

仔细总结了一下,自己的优化基本还是网上流传的那几点

  • 通过 externals 配置来提取常用库,引用cdn
  • 合理配置CommonsChunkPlugin
  • 善用alias
  • dllplugin启用预编译
  • happypack多核构建项目

不过经过自己的实践最后三点是对自己项目优化最大的。文章也主要对后面几点详细说明一下

对了,我项目引用了vue全家桶一套,jquery以及两个第三方插件,element-ui,echarts,自己项目的组件大概有40个左右

原来打包一个项目所需要的时间基本在35-40秒左右(第二次有缓存会稍微快一点),但是偶尔来一次大姨妈,时间甚至要到50s左右,我也是醉了。不过大家可以期待一下经过下面这三步优化大概需要多久。

1.使用dllplugin预编译与引用

首先为什么要引用Dll?在网上浏览了一些文章后,我发现上除了加快构建速度以外,使用webpack的dll还有一个好处。

Dll打包以后是独立存在的,只要其包含的库没有增减、升级,hash也不会变化,因此线上的dll代码不需要随着版本发布频繁更新。 因为使用Dll打包的基本上都是独立库文件,这类文件有一个特性就是变化不大。当我们正常打包这些库文件到一个app.js里的时候,由于其他业务文件的改变,影响了缓存对构建的优化,导致每次都要重新去npm包里寻找相关文件。而使用了DLL之后,只要包含的库没有升级, 增减,就不需要重新打包。这样也提高了构建速度。

那么如何使用Dll去优化项目呢
首先要建立一个dll的配置文件,引入项目所需要的第三方库。这类库的特点是不需要随着版本发布频繁更新,长期稳定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: {
//你需要引入的第三方库文件
vendor: ['vue','vuex','vue-router','element-ui','axios','echarts/lib/echarts','echarts/lib/chart/bar','echarts/lib/chart/line','echarts/lib/chart/pie',
'echarts/lib/component/tooltip','echarts/lib/component/title','echarts/lib/component/legend','echarts/lib/component/dataZoom','echarts/lib/component/toolbox'],
},
output: {
path: path.join(__dirname, 'dist-[hash]'),
filename: '[name].js',
library: '[name]',
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'dll', '[name]-manifest.json'),
filename: '[name].js',
name: '[name]',
}),
]
};

基本配置参数和webpack基本一模一样,相信来看优化的都明白什么意思,我就不解释了。然后执行代码编译文件。(我的配置文件是放在build里面,下方路径根据项目路径需要变动)

1
webpack -p --progress --config build/webpack.dll.config.js

当运行完执行后,会生成两个新文件在目录同级,一个是生成在dist文件夹下的verdor.js,里面是刚刚入口依赖被压缩后的代码;一个是dll文件夹下的verdor-manifest.json,将每个库进行了编号索引,并且使用的是id而不是name。

接下去你只要去你的webpack配置文件的里的plugin中添加一行代码就ok了。

1
2
3
4
5
6
7
8
9
const manifest = require('./dll/vendor-manifest.json');
...
...,
plugin:[
new webpack.DllReferencePlugin({
context: __dirname,
manifest,
}),
]

这时候再执行webpack命令,可以发现时间直接从40秒锐减到了18-20s左右,整整快了一倍有木有(不知道是不是因为自己依赖库太多了才这样的,手动捂脸)。

2.happypack多线程编译

一般node.js是单线程执行编译,而happypack则是启动node的多线程进行构建,大大提高了构建速度。使用方法也比较简单。以我项目为例,在插件中new一个新的happypack进程出来,然后再使用使用loader的地方替换成对应的id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var HappyPack = require('happypack');
...
...
modules:{
rules : [
...
{
test: /\.js$/,
loader:[ 'happypack/loader?id=happybabel'],
include: [resolve('src')]
},
...
]
},
...
...
plugin:[
//happypack对对 url-loader,vue-loader 和 file-loader 支持度有限,会有报错,有坑。。。
new HappyPack({
id: 'happybabel',
loaders: ['babel-loader'],
threads: 4,//HappyPack 使用多少子进程来进行编译
}),
new HappyPack({
id: 'scss',
threads: 4,
loaders: [
'style-loader',
'css-loader',
'sass-loader',
],
})
]

这时候再去执行编译webpack的代码,打印出来的console则变成了另外一种提示。而编译时间大概从20s优化到了15s左右(感觉好像没有网上说的那么大,不知道是不是因为本身js比重占据太大的缘故)。

3.配合resolve,善用alias

本来是没有第三点的,只不过在搜索网上webpack优化相关文章的时候,看到用人提到把引入文件改成库提供的文件(原理我理解其实就是1.先通过resolve指定文件寻找位置,减小搜索范围;2.直接根据alias找到库提供的文件位置)。

vue-cli配置文件中提示也有提到这一点,就是下面这段代码

1
2
3
4
5
6
7
8
9
resolve: {
//自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
extensions: ['.js', '.vue', '.json'],
//模块别名定义,方便后续直接引用别名,无须多写长长的地址
alias: {
'vue$': 'vue/dist/vue.esm.js',//就是这行代码,提供你直接引用文件
'@': resolve('src'),
}
},

然后我将其他所有地方关于vue的引用都替换成了vue$之后,比如

1
2
// import 'vue';
import 'vue/dist/vue.esm.js';

时间竟然到了12s,也是把我吓了一跳。。。

然后我就把jquery,axios,vuex等等全部给替换掉了。。。不过变化没有特别大,大概优化到了11s左右,美滋滋,O(∩_∩)O~~。如果有缓存的情况下,基本上大概在9s左右

4.webpack3升级

本来是没第四点,刚刚看到公众号推出来一篇文章讲到升级到webpack3的一些新优点,比如Scope Hoisting(webpack2升级到webpack3基本上没有太大问题)。通过添加一个新的插件

1
2
3
4
5
// 2017-08-13配合最新升级的webpack3提供的新功能,可以使压缩的代码更小,运行更快
...
plugin : [
new webpack.optimize.ModuleConcatenationPlugin(),
]

不过在添加这行代码之后,构建时间并没有太大变化。因为它的优点是提供js在浏览器中的运行速度。webpack2会把每个处理后的模块用一个函数包裹起来,导致浏览器中的JS执行效率降低,主要是因为闭包函数降低了JS引擎解析速度。

不过在浏览器中国的实际效果感觉不出来太大差别

然后还有一个是webpack3中所有的模块支持用ID进行标记,如果重复引用相同的模块

5.去除不必要的文件

因为要引入代码高亮的highlight.js插件,webpack会引入里面有各个语言的js文件,但是我们项目只需要js,html,css。搜了一下发现网上已经有类似的解决方法了,ContextReplacementPlugin会根据你写的正则去匹配你需要的文件。

而且自己记得webpack3的升级中有个新特性tree shaking就是可以从文件树中去除不必要的文件。

好了基本上感觉就是以上这些效果对项目的优化最大,虽然没有到网上说的那种只要3~4秒时间那么变态,不过感觉基本9-12秒的时间也可以了。