一、vueCli 查看打包后文件的大小占比
??vue-cli2 使用 webpack-bundle-analyzer
// 用vue-cli2 構(gòu)建的項目 中里已經(jīng)集成了
使用npm run build --report 命令即可
??下面適用于:vue-cli3
1.1 安裝依賴
$ npm install webpack-bundle-analyzer --save-dev
1.2 配置vue.config.js
chainWebpack: config => {
// 查看打包文件體積大小
config
.plugin('webpack-bundle-analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin,[
{
analyzerMode: 'server'
}
])
}
/**
analyzerMode?: 'server' | 'static' | 'json' | 'disabled';
* Can be "server", "static" or "disabled".
* Defaults to "server".
* In "server" mode analyzer will start HTTP server to show bundle report.
* In "static" mode single HTML file with bundle report will be generated.
* In "json" mode single JSON file with bundle report will be generated
* In "disabled" mode you can use this plugin to just generate Webpack Stats JSON file by setting "generateStatsFile" to true.
*/
1.3 配置打包腳本
在package.json的scripts中配置
$ "build": "vue-cli-service build --report"
執(zhí)行命令:
$ npm run build
打開瀏覽器:http://127.0.0.1:8888 之后 就會看到一個【可視化】的文件占比

??擴展:終端如果報出警告: (資產(chǎn)大小限制244KIB,可能回影響網(wǎng)絡性能)。

??解決辦法:在vue.config.js中配置
module.exports = {
//webpack配置
configureWebpack: {
//關閉 webpack 的性能提示
performance: {
hints:false
}
// 或者
//警告 webpack 的性能提示
performance: {
hints:'warning',
//入口起點的最大體積
maxEntrypointSize: 50000000,
//生成文件的最大體積
maxAssetSize: 30000000,
//只給出 js 文件的性能提示
assetFilter: function(assetFilename) {
return assetFilename.endsWith('.js');
}
}
},
// vue.config.js
// configureWebpack: config => {
// config.performance = {
// hints: 'warning',
// maxEntrypointSize: 50000000,
// maxAssetSize: 30000000,
// assetFilter: function(assetFilename) {
// return assetFilename.endsWith('.js');
// }
// }
// }
}
更多細節(jié)可參考:webpack中文文檔-性能(performance)
二、移除console
如果你使用的是 webpack v5 或以上版本,你不需要安裝這個插件。
webpack v5 自帶最新的 terser-webpack-plugin。
如果使用 webpack v4,則必須安裝 terser-webpack-plugin v4 的版本。
2.1 安裝依賴
$ npm install terser-webpack-plugin -D
2.2 配置vue.config.js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer([
new TerserPlugin({
test: /\.js(\?.*)?$/i,
terserOptions: {
compress: {
drop_console: true,
pure_funcs: ['console.log']
}
}
})
])
} else {
// disable optimization during tests to speed things up
config.optimization.minimize(false)
}
}
}
更多細節(jié)可參考:webpack中文文檔-TerserWebpackPlugin
???如果報錯:
Error: optimization.minimizer() no longer supports being passed an array. Either switch to the new syntax (https://github.com/neutrinojs/webpack-chain#config-optimization-minimizers-adding) or downgrade to webpack-chain 4. If using Vue this likely means a Vue plugin has not yet been updated to support Vue CLI 4+.
??可重新配置
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer('js')
.use(require.resolve('terser-webpack-plugin'), [{ terserOptions: {
// 打包刪掉注釋
comments: true,
compress: {
drop_console: true,
drop_debugger: true
// pure_funcs: ["console.log"]
}
} }])
} else {
// disable optimization during tests to speed things up
config.optimization.minimize(false)
}
??擴展:為什么刪除生產(chǎn)環(huán)境的console?
console.log:向web開發(fā)控制臺打印一條消息,常用來在開發(fā)時調(diào)試分析。有時在開發(fā)時,需要打印一些對象信息,但發(fā)布時卻忘記去掉console.log語句,這可能造成內(nèi)存泄露。
在傳遞給console.log的對象是不能被垃圾回收 ??,因為在代碼運行之后需要在開發(fā)工具能查看對象信息。所以最好不要在生產(chǎn)環(huán)境中console.log任何對象。

實例代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Leaker</title>
</head>
<body>
<input type="button" value="click">
<script>
!function () {
function Leaker() {
this.init();
};
Leaker.prototype = {
init: function () {
this.name = '*'.repeat(1e5);
console.log("Leaking an object %o: %o", (new Date()), this); // this對象不能被回收
}
};
document.querySelector('input').addEventListener('click', function () {
new Leaker();
}, false);
}()
</script>
</body>
</html>
這里結(jié)合Chrome的Devtools–>Performance做一些分析,操作步驟如下:
- 開啟Performance的記錄
- 執(zhí)行CG按鈕,創(chuàng)建基準參考線
- 多次點擊【click】按鈕,新建Leaker對象
- 執(zhí)行CG按鈕
- 停止記錄

可以看出
【JS Heap】線最后沒有降回到基準參考線的位置,顯然存在沒有被回收的內(nèi)存。如果將代碼修改為
// console.log("Leaking an object %o: %o", (new Date()), this);

重復上述的操作步驟,分析結(jié)果如下:
從對比分析結(jié)果可知,console.log打印的對象是不會被垃圾回收器回收的。
因此最好不要在頁面中console.log任何對象,包括warn、error等兄弟,這樣可能會影響頁面的整體性能,特別在生產(chǎn)環(huán)境中,這些細節(jié)需要特別的關注。
三、壓縮圖片
3.1 安裝依賴
$ npm install terser-webpack-plugin -D
3.2 配置vue.config.js
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
bypassOnDebug: true,
disable: process.env.NODE_ENV !== 'production'
});
四、UI庫按需加載
對于大多數(shù)系統(tǒng)而言,都會使用一些一些UI組件庫,例如Ant Design或者是Element UI,這些組件都是支持按需引入,我們在使用這些組件時,如果只用到了其中一部分組件,可以配置按需加載,在main.js中修改代碼:
import {
Pagination,
Icon,
Tabs,
} from 'ant-design-vue'
// import 'ant-design-vue/dist/antd.css' 已經(jīng)通過babel引入 這里就不全局引入了
Vue.use(Pagination)
.use(Icon)
.use(Tabs)
然后修改babel.config.js,如下:
"plugins": [
["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }], // `style: true` 會加載 less 文件
]
這樣,組件對應的js和css文件就可以實現(xiàn)按需加載.
五、路由懶加載
對于一般比較大型的B端管理系統(tǒng)項目,基本上都會使用Vue Router來管理路由,這些項目涉及的頁面都比較多,所以為了防止首屏資源過大,需要采用路由懶加載資源即Code Splitting,將每個頁面的資源進行分離,這個只需在router.js里配置即可:
// 采用箭頭函數(shù)和import進行懶加載處理
$ component: () => import('./index.vue')
六、moment優(yōu)化
6.1 問題描述
根據(jù)打包分析圖來看,主要是locale下moment的其他語言包占用體積較大。默認是en的語言包,所以在無需其他語言的情況下,可以直接忽略掉locale下的文件不打包。
忽略之前:

忽略之后:

6.2 解決方案
用webpack自帶的IgnorePlugin插件
// vue.config.js
var webpack = require('webpack')
module.exports = {
// ...此處省略其他配置
chainWebpack: config => {
config.plugin('ignore')
.use(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)); //忽略/moment/locale下的所有文件
}
// ...此處省略其他配置
}
6.3 解決方案-原理
在webpack編譯階段, 如果引入的文件路徑匹配/^./locale$/,則會忽略這個文件, 也就不會被打包進去。
- 搜索moment包編譯后的文件并未找到完全匹配/^./locale$/這個正則的引入語句,只有aliasedRequire('./locale/' + name)這條語句和locale相關, 卻又和正則匹配不上, 倒是在moment的src源文件中有import ... from './locale'。 但是在moment的package.json中main是指向編譯后的文件并不是src文件,這就奇了怪了, 于是debug IgnorePlugin看了一下。

- 圖中request真是./locale, 眼瞎了還是webpack的問題?按照dependencies的位置1853行查看moment編譯后的文件, 定位到了確實是 aliasedRequire('./locale/' + name), 怎么回事?

- 原來webpack在編譯時,遇到require('./locale/' + name)此類表達式時,webpack 會查找目錄 './locale/' 下符合正則表達式 /^.*.$/的文件。由于 name 在編譯時還是未知的,webpack 會將每個文件都作為模塊引入到 bundle 中, 這就是為什么引入moment之后, 編譯完的文件為什么會那么大的原因。
6.4 添加IgnorePlugin后, 需要設置locale怎么辦?
- 在添加webpack.IgnorePlugin之后, 文件大小是減小了, 但是在設置moment.locale('zh-cn')之后, format之后的日期仍然是英文的,語言沒有切換過來。
添加之前:打包之后包含momen.js的文件大小

添加之后:打包之后包含momen.js的文件大小

- 功能缺失肯定是不能接受的, 怎么辦?怎么辦?
- 在moment文檔上也提供了解決方案, moment-locales-webpack-plugin
$ npm install --save-dev moment-locales-webpack-plugin
// vue.config.js
new MomentLocalesPlugin({
localesToKeep: ['zh-cn'],
})
// const MomentLocalesPlugin = require('moment-locales-webpack-plugin')
// config.plugin('moment-locales-webpack-plugin').use(
// new MomentLocalesPlugin({
// localesToKeep: ['zh-cn']
// })
// );
- moment默認locale是en,它必然會被打包進去, 如果需要配置其他語言,可以通過localesToKeep來配置, 其他沒用到的語言包也就不會被打包進去了。

vue.config.js配置如下代碼
config.plugin('moment-locales-webpack-plugin').use(
new MomentLocalesPlugin({
localesToKeep: ['es-us', 'ru', 'cs', 'hi', 'uk']
})
);
可以看到打包的時候都被打包刪除掉了!

6.5 moment-locales-webpack-plugin原理分析
- 如果沒有配置option, 用IgnorePlugin忽略所有語言包(en除外)
- 如果設置option, 用 ContextReplacementPlugin插件設置webpack在編譯階段的查找規(guī)則,即查找指定的locale。
...
if (localesToKeep.length > 0) {
var regExpPatterns = localesToKeep.map(function(localeName) {
return localeName + '(\\.js)?';
});
return new ContextReplacementPlugin(
/moment[\/\\]locale/,
new RegExp('(' + regExpPatterns.join('|') + ')$') // 配置webpack編譯階段的查找規(guī)則, 即指定語言包
);
} else {
return new IgnorePlugin(/^\.\/locale$/, /moment$/);
}
...
七、webpack重復打包同名依賴包
最近安裝了webpack-bundle-analyzer插件來分析打包構(gòu)成,發(fā)現(xiàn)有一些包被重復的打包了多次,這樣會讓構(gòu)建出來的包格外的臃腫。這主要是因為我們往往引用了很多的第三方包,而很多工具類的庫也會被別的包間接的依賴,所以就導致了重復打包的現(xiàn)象,例如下圖的bn.js。

7.1 解決方案
在webpack的resolve下面添加如下配置:
// 第一種方法
module.exports = {
resolve: {
alias:{
'bn.js': path.resolve(process.cwd(), 'node_modules', 'bn.js')
}
}
};
// 第二種方法
module.exports = {
configureWebpack:{
resolve:{
alias:{
'bn.js': path.resolve(process.cwd(), 'node_modules', 'bn.js')
}
}
}
};
// 第三種方法
const path = require('path');//引入path模塊
function resolve(dir){
return path.join(__dirname,dir)//path.join(__dirname)設置絕對路徑
}
module.exports={
chainWebpack: config =>{
config.resolve.alias
.set("@", resolve("src"))
}
}
resolve.alias的作用其實就是添加包的別名并強行指定引用統(tǒng)一路徑,配置完的效果如下,只能看到一個bn.js了。
優(yōu)化之后


優(yōu)化之前


八、有選擇的使用prefetch和preload
prefetch
<link rel="prefetch" ></link>
這段代碼告訴瀏覽器,這段資源將會在未來某個導航或者功能要用到,但是本資源的下載順序權(quán)重比較低。也就是說prefetch通常用于加速下一次導航,而不是本次的。
preload
<link rel="preload" ></link>
preload通常用于本頁面要用到的關鍵資源,包括關鍵js、字體、css文件。preload將會把資源得下載順序權(quán)重提高,使得關鍵數(shù)據(jù)提前下載好,優(yōu)化頁面打開速度。
在使用Vue Cli生成的項目里,當我們配置了路由懶加載后,默認情況下webpack在構(gòu)建時會對所有的懶加載資源進行prefetch和preload,所以當你打開首頁時,會看到大量的prefetch和preload請求,如下圖:

// 禁止prefetch和preload
chainWebpack: (config) => {
config.plugins.delete('prefetch')
config.plugins.delete('preload')
}
// 有選擇的prefetch和preload
config.plugin('prefetch').tap(options => {
options[0].fileBlacklist = options[0].fileBlacklist || []
options[0].fileBlacklist.push(/myasyncRoute(.)+?\.js$/)
return options
})
上面代碼修改vue.config.js的chainWebpack來添加配置。
總結(jié):大功告成????????????????????????????????????????
參考鏈接:
https://segmentfault.com/a/1190000012295395
https://zhuanlan.zhihu.com/p/362547907
http://m.itdecent.cn/p/4f8f36944a46
https://juejin.cn/post/6844903987632685063
https://blog.csdn.net/u010352770/article/details/101538528