vue項目性能優(yōu)化方案

背景
項目采用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)化的一些處理,大家可以參考下。

如有問題,請指出,接收批評。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容