【补充】基于vue-cli创建的项目进行打包优化
一、前言
❝「阅读完该文章大概需要2.5min。」
❞
「读完该篇文章你能学到」
vue-cli
默认做了哪些优化?在cli的基础上我们又能做哪些优化? vue.config.js
中如何配置一些常用的plugin
和loader
vue-cli
的出现,让我们省掉了配置webpack的时间。也就是说,一个不懂webpack的人,也能直接上手开发。比如file-loader
,url-loader
会提前为我们配置好。「性能方面」,
vue-cli
也默认尽可能多的帮我们做了优化,比如cache-loader会在项目中预先做了配置。我们可以在控制台输入vue inspect > webpack.config.js
,即可在webpack.config.js文件中查看cli预先定义好的基础配置。我们今天就在vue-cli搭建好的项目基础上聊一聊可优化的点。
二、项目源码
❝本文所用到的项目源码
❞
三、量化指标
1. build时间
❝❞
speed-measure-webpack-plugin
插件可以在build的时候看到webpack的loader和plugin所用的时间,配置非常简单。如下:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('speed')
.use(SpeedMeasureWebpackPlugin)
}
}
❝顺便看一下效果
❞
2.build后包的大小以及包的多少
❝❞
webpack-bundle-analyzer
插件可以帮我们可视化的展示build时的每个包的大小以及依赖。vue-cli也帮我们做了默认的配置,我只需要在build的后面加一个参数--report即可。
// package.json
{
"name": "dll-vue",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"report": "vue-cli-service build --report",
}
}
❝❞
npm run report
之后,dist目录下就多了一个report.html文件,我们用浏览器打开这个文件看一下,右上角那个小蓝块是我们的vue代码,接下来我们主要来优化小蓝块之外的代码
四、开始优化
1. include/exclude
我们通常配置include和exclude,来达到使loader仅仅处理匹配到的文件,像这样
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js[x]?$/,
use: ['babel-loader'],
include: [path.resolve(__dirname, 'src')]
}
]
},
}
而vue的transpileDependencies
属性默认情况下 babel-loader
会忽略所有 node_modules
中的文件,其实已经满足了我们大部分需求。
2. resolve
❝resolve: 配置
❞webpack
如何寻找模块所对应的文件,比如import * from 'xxx',xxx模块应该优先从node_modules中找,我们通过vue inspect > webpack.config.js
导出的文件中在modules字段中可以清晰的看到已经将node_modules设置为优先寻找的模块
resolve: {
// ...
modules: [
'node_modules',
...
]
// ...
}
3. happypack-plugin/thread-loader
❝一般来说,我们可以通过happack-plugin或者thread-loader开启多线程打包。vue-cli的
❞parallel
属性的含义是:是否为 Babel 或 TypeScript 使用thread-loader
,默认值为cpu的内核数,也就是说如果你系统是3核cpu,则build的时候,会自动在babel-loader和ts-loader执行时候开启3个线程。如果你想试着自己的配置一下,可以像下面这样。(不过我试着自己配置了之后,似乎没什么效果。也许是我配置的不对,欢迎大家来指正)
config.module.rule('vue')
.use('thread-loader')
.loader('thread-loader')
.before('vue-loader')
4. noParse
❝如果一些第三方模块没有AMD/CommonJS规范版本,可以使用
❞noParse
来标识这个模块,这样Webpack
会引入这些模块,但是不进行转化和解析,从而提升Webpack
的构建性能 ,例如:jquery
、lodash
,vue.config.js
中可以这样配置:
// vue.config.js
module.exports = {
//...
configureWebpack:{
module: {
noParse: /^(lodash|moment)$/
}
}
//...
}
5. ContextReplacementPlugin
❝一些依赖,我们也许只是用到了一部分,没必要全部解析,比如moment中的语言包,我们一般只用中文包就够了,所以可以这样配置:
❞
// vue.config.js
module.exports = {
//...
configureWebpack:{
plugins: [
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn/)
]
}
//...
}
❝我们通过一张图片来体验一下, 左边为去掉语言包的效果
❞
6. externals
❝一般来说像jQuery这些第三方的包,我们采用CDN的方式来引入,像这样
❞
<-- public/index.html -->
<script src="https://code.jquery.com/jquery-3.6.0.min.> js"></script>
❝在使用的时候,我们希望通过import的方式引入,像这样
❞
// App.vue\
import $ from 'jquery'
$('.today').text = 'today'
❝这就造成一个问题,build的时候就会将jquery再打包一次 我们可以配置
❞externals
来达到build时忽略掉指定的依赖
// vue.config.js
module.exports = {
//...
configureWebpack:{
config.externals = {
jquery: 'jQuery'
}
}
//...
}
7. uglifyjs-webpack-plugin
❝在build的时候可以压缩代码的大小,有几个常用的配置可以单独提一下,比如去掉console.log,比如进行多进程压缩。可以这样:
❞
// vue.config.js
module.exports = {
//...
configureWebpack:{
config.optimization.minimizer = [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
drop_console: true, // 去掉console
}
parallel: true, //默认并发运行数:os.cpus().length - 1
}
})
]
}
//...
}
8. compression-webpack-plugin
❝我们可以将代码压缩为.gz文件,浏览器也是可以识别的。可以这样配置
❞
//vue.config.js
module.exports = {
//...
configureWebpack:{
plugins: [
new CompressionWebpackPlugin()
]
}
//...
}
9. DllPlugin 和 DllReferencePlugin
对于变化几率很小的一些第三方包,其实没必要build的时候都要打包一次, 可以把这些第三方包单独抽离出来,提前打包好。 webpack本身是要体现出模块间的依赖关系,当我们将一些包抽离出来后,维护之前的依赖关系就需要 manifest.json
这个文件。让我们从接下来的实战中来学习它。
❝<1> 新建一个配置文件webpack.dll.config.js
❞
// webpack.dll.config.js
module.exports = {
mode: 'production',
entry: {
vue_vendor: ['vue/dist/vue.runtime.esm.js', 'vuex', 'vue-router', 'element-ui'],
other_vendor: ['lodash', 'moment']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, './public/dll'),
library: '[name]_[hash]'
},\
plugins: [
new webpack.DllPlugin({
name: '[name]_[hash]',
path: path.resolve(__dirname, '[name].manifest.json')
})
]
}
❝<2> 为了方便,我们将读取该配置文件的命令写到package.json文件中,像这样。(需要安装webpack-cli)
❞
// package.json
{
.....
"scripts": {
.....
"dll": "webpack --config ./webpack.dll.config.js"
}
.....
}
❝<3> 执行
❞npm run dll
后可以看到生成两个manifest文件,像这样
❝<4> 修改vue.config.js,引入依赖文件,并自动将dll下的文件插入到index.html中(这里我们引入一个插件
❞add-asset-html-webpack-plugin
)
module.exports = {
chainWebpack: config => {
// 多个manifest.json文件就需要写多次
config.plugin('vendorDll1')
.use(webpack.DllReferencePlugin, [
{
context: __dirname,
manifest: require('./public/manifest/other_vendor.manifest.json')
}
])
config.plugin('vendorDll2')
.use(webpack.DllReferencePlugin, [
{
context: __dirname,
manifest: require('./public/manifest/vue_vendor.manifest.json')
}
])
// 将dll下的文件自动插入到index.html中
config.plugin('asset')
.use(AddAssetHtmlWebpackPlugin, [
[
{
filepath: path.resolve(__dirname, 'public/dll/vue_vendor.dll.js'),
outputPath: 'dll',
publicPath: '/dll'
},
{
filepath: path.resolve(__dirname, 'public/dll/other_vendor.dll.js'),
outputPath: 'dll',
publicPath: '/dll'
}
]
])
}
}
10. optimization.splitChunks
❝抽离公共代码,通过配置splitChunks可抽离公共的代码,防止重复,我没有在自己的项目中用,
❞
//vue.config.js
module.exports = {
//...
chainWebpack: config => {
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {}
})
}
//...
}
五、效果展示
六、补充几个不需要配置的优化点
1. extensions
❝当我们导入模块时,假如没有指定后缀,期望优先匹配的文件格式,我们直接看vue-cli默认的配置
❞extensions: ['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm']
2. tree-shaking
❝我们通过import方式引入,webpack会自动移除掉没有用到的模块代码
❞
3. Scope hosting
❝作用域提升:比如
❞
let a = 1
let b = 2
let c = a + b
// webpack自动优化为
c = 3
4. 路由懒加载
❝通过() => import('xxx')方式引入,可达到路由懒加载的效果
❞
六、总结
「该文章为最近优化团队项目的webpack打包速度,做的总结。」
「如果有不对的地方欢迎大家在讨论区纠正」
「欢迎关注公众号:
前端小卡
」「如果觉得有用,欢迎点个赞啦」