背景:
項目采用vue cli3搭建逛漫,集成前端組件以及地圖效果黑低,導致項目打包后資源包文件特別大,打包速度慢酌毡,首屏渲染耗時長克握,甚至出現(xiàn)左右界面圖表數(shù)據(jù)不渲染的問題。
優(yōu)化前準備:
首先我們需要先排查影響性能枷踏、導致打包資源文件過大的原因菩暗,以及代碼的使用率
- webpack-bundle-analyzer:
npm install webpack-bundle-analyzer
// vue.config.js文件(vue cli3根目錄下的文件,如果沒有旭蠕,可創(chuàng)建此文件停团,用于webpack配置)
module.exports = {
chainWebpack: config => {
/* 添加分析工具 */
if (process.env.NODE_ENV === 'production') {
if (process.env.npm_config_report) {
config
.plugin('webpack-bundle-analyzer')
// eslint-disable-next-line global-require
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
.end();
config.plugins.delete('prefetch');
}
}
},
};
通過命令 npm run build --report 會在打包完成后本地啟動一個服務,自動打開127.0.0.1:8888網(wǎng)頁掏熬,可以查看打包后各個依賴包占用的資源大小佑稠,我們可以針對各個依賴包的相關大小作出體積的優(yōu)化,如果開源庫過大可以考慮按需引入旗芬,不要全部引入舌胶,如果是自己公司封裝的私有組件庫、類庫疮丛,可以排查哪部分占用文件過大幔嫂,打包進行優(yōu)化處理,如下圖:
-
coverage:
通過打開控制臺(F12)誊薄,ctrl+shift+p履恩,搜索coverage,然后選中暇屋,然后點擊開始錄制似袁,之后做一些操作洞辣,比如刷新界面咐刨,開始錄制昙衅,查看我們代碼的實際使用率,也可以點擊文件查看這個文件中具體未使用的那段代碼(紅色部分)
優(yōu)化方案:
gzip壓縮:gzip壓縮可以特別明顯的提高我們的代碼加載效果定鸟,提升效率5-6倍左右而涉,它會把諸如js、css等文件進行壓縮联予,并且讓我們在加載時去請求那些gz文件而提升請求效率啼县,這部分可以參考博主的另一篇文章 gzip壓縮方案
路由懶加載:實際來說,我們在首頁就不需要加載其他路由的文件以及數(shù)據(jù)沸久,而當我們執(zhí)行到某個具體路由季眷,再去加載當前路由的才是最正確的方案,所以路由懶加載是我們必須要做的卷胯,使用方法如下:
這里拿home路由舉例子刮,博主每個頁面都是分為左、右窑睁、中上挺峡、中下四個模塊的,所以每個路由中都有四個組件担钮,當然橱赠,路由懶加載寫法是一樣的,通過箭頭函數(shù)返回一個組件箫津,webpackChunkName就是最早的打包后的文件名狭姨,同一路由可以寫同一個名字,推薦寫路由名苏遥,方便我們知道是哪個路由下的送挑。
const MainLeft = () => import(/* webpackChunkName: "Home" */ '../components/Home/MainLeft.vue');
const MainRight = () => import(/* webpackChunkName: "Home" */ '../components/Home/MainRight.vue');
const MainCenterTop = () => import(/* webpackChunkName: "Home" */ '../components/Home/MainCenterTop.vue');
const MainCenterBottom = () => import(/* webpackChunkName: "Home" */ '../components/Home/MainCenterBottom.vue');
而在router.js里就是正常寫法:
routes: [
{
path: '/',
name: 'Home',
title: '首頁',
components: {
routerLeft: Home.MainLeft,
routerRight: Home.MainRight,
routerCenterTop: Home.MainCenterTop,
// routerCenterBottom: Home.MainCenterBottom,
},
},
]
開啟路由懶加載后,vue cli3項目還需要在vue.config.js文件中配置如下
// vue.config.js文件(vue cli3根目錄下的文件暖眼,如果沒有惕耕,可創(chuàng)建此文件,用于webpack配置)
module.exports = {
chainWebpack: config => {
// 移除 prefetch 插件(避免會預先加載模塊/路由)
config.plugins.delete('prefetch');
},
};
(注:之前博主遇到個小坑诫肠,項目中有個文件夾司澎,里面寫了一些全局封裝并且全局注冊的組件供其他頁面使用,而有些組件中使用了require去動態(tài)加載圖片導致打包后路由懶加載不生效沒有生成對應路由的文件栋豫,大家留意一下即可挤安,如果遇到此問題,可以找博主提供解決思路)
- 組件庫按需引入:這一點不需要過多贅述了丧鸯,使用了諸如element ui等組件庫的話蛤铜,官網(wǎng)都有很詳細的介紹如何按需加載
- 圖片壓縮:安裝此插件的時候有可能出現(xiàn)部分依賴安裝失敗,導致壓縮失敗,可以先卸載此插件然后用cnpm或淘寶源重新安裝
npm install image-webpack-loader
// vue.config.js文件(vue cli3根目錄下的文件围肥,如果沒有剿干,可創(chuàng)建此文件,用于webpack配置)
module.exports = {
chainWebpack: config => {
// 開啟圖片壓縮
// config.module
// .rule('images')
// .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
// .use('image-webpack-loader')
// .loader('image-webpack-loader')
// .options({ bypassOnDebug: true });
},
};
- map映射文件:vue打包會保留源文件的map映射穆刻,方便我們打包部署后依然可以通過控制臺的source查看搜索源文件的代碼置尔,方便定位問題,但是多生成map文件會導致打包文件過大氢伟,所以仁者見仁智者見智榜轿,看各位是否需要打包部署后方便定位問題吧,不需要的話可以考慮不生成map文件
module.exports = {
productionSourceMap: true,
}
- 分包加載:vue打包后會把依賴包都打包到app.js文件里朵锣,這樣會導致單文件過大谬盐,請求耗時,加載緩慢诚些,所以可以考慮把各依賴單獨拆分打包设褐,實現(xiàn)多個小文件請求加載的方式,代碼如下:
// vue.config.js文件(vue cli3根目錄下的文件泣刹,如果沒有助析,可創(chuàng)建此文件,用于webpack配置)
module.exports = {
configureWebpack: {
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 20000,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
},
},
};
上述代碼會把所有的包括異步請求的模塊分割椅您,針對大于20000k的文件外冀,最終命名方式是npm.依賴包.js文件,比如npm.echarts.js掀泳,命名可以自定義雪隧。
- 重復引用依賴包:封裝了一個公共組件庫(可通過npm下載),但是臨時為了省事項目中也封裝了一些組件單獨放在一個文件夾中员舵,最終一些相同的依賴就會被重復引用脑沿,比如組件庫中用了amcharts圖表,而項目中又安裝使用了這個圖表庫马僻,會導致此依賴被重復引用庄拇,解決方案:把項目中的組件挪到組件庫中,這樣同一份依賴就只會被使用一次韭邓。(不過我記得webpack有配置可以針對同一依賴多次引用的處理措近,把依賴進行緩存,避免被多次引用女淑,感興趣的童鞋可以去查閱下相關資料)
- fps:之前項目中封裝了一個方法瞭郑,針對數(shù)據(jù)請求多的情況,做了fps處理鸭你,如果fps小于30就先不執(zhí)行數(shù)據(jù)請求屈张,先等現(xiàn)有數(shù)據(jù)擒权、界面、地圖等渲染完畢阁谆,fps穩(wěn)定后再去執(zhí)行下一部分數(shù)據(jù)請求碳抄、界面渲染,本身是為了能更好更流暢的渲染笛厦,但是如果電腦性能本身就很差纳鼎,fps本身就很低俺夕,會導致一直數(shù)據(jù)不渲染(哈哈哈裳凸,給自己埋了個坑),最終定位問題后發(fā)現(xiàn)了這段不合理的處理劝贸。大家寫代碼也要多注意這些情況姨谷。
完整的vue.config.js配置:
const fs = require('fs');
const CompressionPlugin = require('compression-webpack-plugin');
let devServer = {};
if (fs.existsSync('./dev-config.js')) {
// eslint-disable-next-line global-require
devServer = require('./dev-config');
} else {
console.error('!!!please create the dev-config.js file from dev-config-template.js');
}
module.exports = {
lintOnSave: true,
productionSourceMap: true,
chainWebpack: config => {
// 移除 prefetch 插件(避免會預先加載模塊/路由)
config.plugins.delete('prefetch');
// Loader
config.module
.rule('svg')
.test(/\.(swf|ttf|eot|svg|woff(2))(\?[a-z0-9]+)?$/)
.use('file-loader')
.loader('file-loader')
.end();
// 開啟圖片壓縮
// config.module
// .rule('images')
// .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
// .use('image-webpack-loader')
// .loader('image-webpack-loader')
// .options({ bypassOnDebug: true });
/* 添加分析工具 */
if (process.env.NODE_ENV === 'production') {
if (process.env.npm_config_report) {
config
.plugin('webpack-bundle-analyzer')
// eslint-disable-next-line global-require
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
.end();
config.plugins.delete('prefetch');
}
}
},
devServer,
configureWebpack: {
resolve: {
alias: {
src: '@',
components: '@/components',
views: '@/views',
},
},
plugins: [
new CompressionPlugin({
algorithm: 'gzip', // 使用gzip壓縮
test: /\.js$|\.html$|\.css$/, // 匹配文件名
filename: '[path].gz[query]', // 壓縮后的文件名(保持原文件名,后綴加.gz)
minRatio: 1, // 壓縮率小于1才會壓縮
threshold: 10240, // 對超過10k的數(shù)據(jù)壓縮
deleteOriginalAssets: false, // 是否刪除未壓縮的源文件映九,謹慎設置梦湘,如果希望提供非gzip的資源,可不設置或者設置為false(比如刪除打包后的gz后還可以加載到原始資源文件)
}),
],
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 20000,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
},
},
};
好了件甥,以上就是博主自身項目中關于性能優(yōu)化的一些處理捌议,大家可以參考下。
如有問題引有,請指出瓣颅,接收批評。