前言
Vue-cli3提供了开箱即用的项目脚手架,自带的webpack配置已经经过了优化。但是当项目越来越庞大的时候,目前我的项目打个包都要三四分钟,并且首页加载也很慢。于是希望对webpack进行优化,进一步提升打包效率。
但是目前网上的各种资料,要么是针对webpack4之前版本的配置,要么是Vue-cli3默认配置已经包含了的内容。最后经过实践,本着宁缺毋滥的原则,我主要使用了以下插件:webpack-bundle-analyzer
分析包大小、DllPlugin
提前打包、gzip
代码压缩。由于我的水平有限,有不正确的地方欢迎指正。
Vue-cli3已有的优化
首先,通过官方文档中的配置参考可以了解到一部分默认配置。通过 vue-inspect
命令,可以查看默认的webpack配置。例如babel-loader
,已经配置了exclude
,并且开启了cacheDirecorty
,那么不用考虑优化loader了。
其次,从Vue-cli官方文档中,注意到parallel这个配置有如下说明。
是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
由此可见,网上很多教程中的多线程打包插件HappyPack
也不需要了。
此外,webpack4中,mode
为production
时会自动开启代码压缩和Tree Shaking
。
进一步优化
既然Vue-cli3已经提供了以上这些优化,那么能做的也很有限了,于是我从提前打包和开启gzip压缩两个方面来进行进一步优化。
提前打包
注意到打包后的项目中,有一个vendors.js文件体积特别大,有1.8M,而这个文件和引入的第三方库有关。我的项目引入了很多第三方库,例如ant-design-vue
、antv
、lodash
等等,这些库文件一般是不会变动的,那么通过提前打包的方式,是不是就能缩短打包时间了?
接下来分析第三方库。由于我使用第三方库是按需引入的,不知道引入的哪些库比较占体积,这里就需要使用webpack-bundle-analyzer
这个插件,它是通过矩形树图的方式,显示各个包中模块的大小和关系。通过插件的方式引入
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
configureWebpack: config => {
if (process.ENV.NODE_ENV === 'production') {
config.plugins = [
...config.plugins,
new BundleAnalyzerPlugin()
]
}
}
}
运行npm run build
后,打开localhost:8888
,下图就是我的项目结构
ant-design-vue
、moment
、antv
和quill
都是比较占体积的,可以考虑进行拆分打包,而lodash
由于按需引入的原因,并没有占很大体积,所以不需要单独拆分。
在项目根目录创建webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
// dll文件存放的目录
const dllPath = 'dll/'
const libs = {
'ui': ['ant-design-vue'],
'frame': ['vue', 'vuex', 'vue-router', 'moment', 'quill'],
'chart': ['@antv/g2', '@antv/data-set']
}
module.exports = {
entry: {
// 需要提取的库文件
...libs
},
output: {
path: path.join(__dirname, dllPath),
filename: '[name].dll.js',
// vendor.dll.js中暴露出的全局变量名
// 保持与 webpack.DllPlugin 中名称一致
library: '[name]_[hash]'
},
plugins: [
// manifest.json 描述动态链接库包含了哪些内容
new webpack.DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// 保持与 output.library 中名称一致
name: '[name]_[hash]',
context: process.cwd()
})
]
}
在package.json
中加入,并运行npm run dll
"scripts": {
...
"dll": "webpack -p --progress --config ./webpack.dll.conf.js"
},`
在vue.config.js
中加入以下配置
config.plugins = [
...config.plugins,
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require('./dll/ui-manifest.json')
}),
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require('./dll/frame-manifest.json')
}),
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require('./dll/chart-manifest.json')
})
]
打包完成了,但是webpack是不会帮我们自动引入这些单独打包的第三方库的,这里就需要用到add-asset-html-webpack-plugin
这个插件。
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
config.plugins = [
...config.plugins,
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, './dll/*.js'), // dll文件位置
publicPath: './js', // dll 引用路径
outputPath: './js' // dll最终输出的目录
})
]
开启gzip压缩
经过gzip压缩的包可以缩小约2/3体积,对网络传输的提升显著。开启gzip需要服务器的配合,以nginx为例,在nginx.conf
中加入
server: {
...
gzip on; # 是否开启gzip
gzip_buffers 32 4K; #缓冲(压缩在内存中缓冲几块? 每块多大?)
gzip_comp_level 6; #推荐6 压缩级别(级别越高,压的越小,越浪费CPU计算资源)
gzip_min_length 100; #开始压缩的最小长度(再小就不要压缩了,意义不在)
gzip_types application/javascript text/css text/xml; #对哪些类型的文件用压缩 如txt,xml,html ,css
gzip_disable "MSIE [1-6]\."; #配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
gzip_vary on; #是否传输gzip压缩标志
}
这样,客户端请求文件时,服务器就会自动压缩文件。但是服务器的压缩是需要CPU开销的,为了节省CPU资源,我们可以在打包的时候同时打包一份gzip,配置如下
const CompresssionPlugin = require('compression-webpack-plugin')
config.plugins = [
...config.plugins,
new CompressionPlugin({
test: /\.js$|\.html$|\.css$/,
deleteOriginalAssets: false // 是否删除原文件
})
]
结果
经过优化后,项目打包时间最短到过90s,但是有时候还是会超过三分钟,不确定是什么原因。 首页加载时间从8s缩短到5s,有了明显的提升。
感谢阅读,文中如有不正确,欢迎指正和探讨。