背景:
項目采用vue cli3搭建,集成前端組件以及地圖效果,導(dǎo)致項目打包后資源包文件特別大,打包速度慢,首屏渲染耗時長,甚至出現(xiàn)左右界面圖表數(shù)據(jù)不渲染的問題。
優(yōu)化前準備:
首先我們需要先排查影響性能、導(dǎo)致打包資源文件過大的原因,以及代碼的使用率
- 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 會在打包完成后本地啟動一個服務(wù),自動打開127.0.0.1:8888網(wǎng)頁,可以查看打包后各個依賴包占用的資源大小,我們可以針對各個依賴包的相關(guān)大小作出體積的優(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 插件(避免會預(yù)先加載模塊/路由)
config.plugins.delete('prefetch');
},
};
(注:之前博主遇到個小坑,項目中有個文件夾,里面寫了一些全局封裝并且全局注冊的組件供其他頁面使用,而有些組件中使用了require去動態(tài)加載圖片導(dǎo)致打包后路由懶加載不生效沒有生成對應(yīng)路由的文件,大家留意一下即可,如果遇到此問題,可以找博主提供解決思路)
- 組件庫按需引入:這一點不需要過多贅述了,使用了諸如element ui等組件庫的話,官網(wǎng)都有很詳細的介紹如何按需加載
- 圖片壓縮:安裝此插件的時候有可能出現(xiàn)部分依賴安裝失敗,導(dǎo)致壓縮失敗,可以先卸載此插件然后用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文件會導(dǎo)致打包文件過大,所以仁者見仁智者見智,看各位是否需要打包部署后方便定位問題吧,不需要的話可以考慮不生成map文件
module.exports = {
productionSourceMap: true,
}
- 分包加載:vue打包后會把依賴包都打包到app.js文件里,這樣會導(dǎo)致單文件過大,請求耗時,加載緩慢,所以可以考慮把各依賴單獨拆分打包,實現(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,命名可以自定義。
- 重復(fù)引用依賴包:封裝了一個公共組件庫(可通過npm下載),但是臨時為了省事項目中也封裝了一些組件單獨放在一個文件夾中,最終一些相同的依賴就會被重復(fù)引用,比如組件庫中用了amcharts圖表,而項目中又安裝使用了這個圖表庫,會導(dǎo)致此依賴被重復(fù)引用,解決方案:把項目中的組件挪到組件庫中,這樣同一份依賴就只會被使用一次。(不過我記得webpack有配置可以針對同一依賴多次引用的處理,把依賴進行緩存,避免被多次引用,感興趣的童鞋可以去查閱下相關(guān)資料)
- fps:之前項目中封裝了一個方法,針對數(shù)據(jù)請求多的情況,做了fps處理,如果fps小于30就先不執(zhí)行數(shù)據(jù)請求,先等現(xiàn)有數(shù)據(jù)、界面、地圖等渲染完畢,fps穩(wěn)定后再去執(zhí)行下一部分數(shù)據(jù)請求、界面渲染,本身是為了能更好更流暢的渲染,但是如果電腦性能本身就很差,fps本身就很低,會導(dǎo)致一直數(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 插件(避免會預(yù)先加載模塊/路由)
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, // 是否刪除未壓縮的源文件,謹慎設(shè)置,如果希望提供非gzip的資源,可不設(shè)置或者設(shè)置為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('@', '')}`;
},
},
},
},
},
},
};
好了,以上就是博主自身項目中關(guān)于性能優(yōu)化的一些處理,大家可以參考下。
如有問題,請指出,接收批評。