前端性能優(yōu)化-webpack

一、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.jsonscripts中配置

$ "build": "vue-cli-service build --report"

執(zhí)行命令:

$ npm run build

打開瀏覽器:http://127.0.0.1:8888 之后 就會看到一個【可視化】的文件占比

20200428150718890.png

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


[圖片上傳中...(20200428150718890.png-e3528-1624502443756-0)]

??解決辦法:在vue.config.js中配置

module.exports = {
    //webpack配置
    configureWebpack: {
        //關(guān)閉 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');
    //          }
    //      }
    //    }
}

更多細(xì)節(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)
        }
      }
}

更多細(xì)節(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)
        }

??擴(kuò)展:為什么刪除生產(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任何對象培慌。


下載 (3).png

實例代碼:

<!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做一些分析,操作步驟如下:

  1. 開啟Performance的記錄
  2. 執(zhí)行CG按鈕柑爸,創(chuàng)建基準(zhǔn)參考線
  3. 多次點擊【click】按鈕吵护,新建Leaker對象
  4. 執(zhí)行CG按鈕
  5. 停止記錄

1069473253-5a266ef78ede4_fix732 (1).png

可以看出【JS Heap】線最后沒有降回到基準(zhǔn)參考線的位置,顯然存在沒有被回收的內(nèi)存表鳍。如果將代碼修改為

// console.log("Leaking an object %o: %o", (new Date()), this);
1879498799-5a266f18551bf_fix732.png

重復(fù)上述的操作步驟馅而,分析結(jié)果如下:

從對比分析結(jié)果可知,console.log打印的對象是不會被垃圾回收器回收的譬圣。
因此最好不要在頁面中console.log任何對象瓮恭,包括warn、error等兄弟厘熟,這樣可能會影響頁面的整體性能屯蹦,特別在生產(chǎn)環(huán)境中,這些細(xì)節(jié)需要特別的關(guān)注绳姨。

三登澜、壓縮圖片


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 文件

  ]

這樣,組件對應(yīng)的js和css文件就可以實現(xiàn)按需加載.

五廓旬、路由懶加載


對于一般比較大型的B端管理系統(tǒng)項目哼审,基本上都會使用Vue Router來管理路由,這些項目涉及的頁面都比較多孕豹,所以為了防止首屏資源過大涩盾,需要采用路由懶加載資源即Code Splitting,將每個頁面的資源進(jìn)行分離励背,這個只需在router.js里配置即可:

// 采用箭頭函數(shù)和import進(jìn)行懶加載處理
$ component: () => import('./index.vue')

六春霍、moment優(yōu)化


6.1 問題描述

根據(jù)打包分析圖來看,主要是locale下moment的其他語言包占用體積較大叶眉。默認(rèn)是en的語言包址儒,所以在無需其他語言的情況下芹枷,可以直接忽略掉locale下的文件不打包。

忽略之前:

image.png

忽略之后:

image.png

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$/莲趣,則會忽略這個文件鸳慈, 也就不會被打包進(jìn)去。

  • 搜索moment包編譯后的文件并未找到完全匹配/^./locale$/這個正則的引入語句喧伞,只有aliasedRequire('./locale/' + name)這條語句和locale相關(guān), 卻又和正則匹配不上走芋, 倒是在moment的src源文件中有import ... from './locale'。 但是在moment的package.json中main是指向編譯后的文件并不是src文件潘鲫,這就奇了怪了, 于是debug IgnorePlugin看了一下翁逞。
image.png
  • 圖中request真是./locale, 眼瞎了還是webpack的問題溉仑?按照dependencies的位置1853行查看moment編譯后的文件挖函, 定位到了確實是 aliasedRequire('./locale/' + name), 怎么回事浊竟?
image.png
  • 原來webpack在編譯時挪圾,遇到require('./locale/' + name)此類表達(dá)式時,webpack 會查找目錄 './locale/' 下符合正則表達(dá)式 /^.*.$/的文件逐沙。由于 name 在編譯時還是未知的,webpack 會將每個文件都作為模塊引入到 bundle 中洼畅, 這就是為什么引入moment之后吩案, 編譯完的文件為什么會那么大的原因。

6.4 添加IgnorePlugin后, 需要設(shè)置locale怎么辦?

  1. 在添加webpack.IgnorePlugin之后帝簇, 文件大小是減小了徘郭, 但是在設(shè)置moment.locale('zh-cn')之后, format之后的日期仍然是英文的丧肴,語言沒有切換過來残揉。

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


image.png

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


image.png
  1. 功能缺失肯定是不能接受的, 怎么辦芋浮?怎么辦抱环?
  2. 在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']
        //     })
        // );
  1. moment默認(rèn)locale是en,它必然會被打包進(jìn)去, 如果需要配置其他語言纸巷,可以通過localesToKeep來配置镇草, 其他沒用到的語言包也就不會被打包進(jìn)去了。
下載 (3).png

vue.config.js配置如下代碼

config.plugin('moment-locales-webpack-plugin').use(
            new MomentLocalesPlugin({
                localesToKeep: ['es-us', 'ru', 'cs', 'hi', 'uk']
            })
        );

可以看到打包的時候都被打包刪除掉了!


[圖片上傳中...(下載 (3).png-2486c4-1624615311186-0)]

6.5 moment-locales-webpack-plugin原理分析

  1. 如果沒有配置option瘤旨, 用IgnorePlugin忽略所有語言包(en除外)
  2. 如果設(shè)置option梯啤, 用 ContextReplacementPlugin插件設(shè)置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重復(fù)打包同名依賴包


最近安裝了webpack-bundle-analyzer插件來分析打包構(gòu)成,發(fā)現(xiàn)有一些包被重復(fù)的打包了多次察滑,這樣會讓構(gòu)建出來的包格外的臃腫打厘。這主要是因為我們往往引用了很多的第三方包,而很多工具類的庫也會被別的包間接的依賴杭棵,所以就導(dǎo)致了重復(fù)打包的現(xiàn)象婚惫,例如下圖的bn.js。


截屏2021-06-26 上午10.55.07.png

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)設(shè)置絕對路徑
}
module.exports={
    chainWebpack: config =>{
        config.resolve.alias
        .set("@", resolve("src")) 
    }
}

resolve.alias的作用其實就是添加包的別名并強(qiáng)行指定引用統(tǒng)一路徑魂爪,配置完的效果如下先舷,只能看到一個bn.js了。

優(yōu)化之后

image.png

image.png

優(yōu)化之前

image.png

image.png

八滓侍、有選擇的使用prefetch和preload


prefetch

<link rel="prefetch" ></link>

這段代碼告訴瀏覽器蒋川,這段資源將會在未來某個導(dǎo)航或者功能要用到,但是本資源的下載順序權(quán)重比較低撩笆。也就是說prefetch通常用于加速下一次導(dǎo)航捺球,而不是本次的。
preload

<link rel="preload" ></link>

preload通常用于本頁面要用到的關(guān)鍵資源夕冲,包括關(guān)鍵js氮兵、字體、css文件歹鱼。preload將會把資源得下載順序權(quán)重提高泣栈,使得關(guān)鍵數(shù)據(jù)提前下載好,優(yōu)化頁面打開速度弥姻。

在使用Vue Cli生成的項目里南片,當(dāng)我們配置了路由懶加載后,默認(rèn)情況下webpack在構(gòu)建時會對所有的懶加載資源進(jìn)行prefetch和preload庭敦,所以當(dāng)你打開首頁時疼进,會看到大量的prefetch和preload請求,如下圖:

v2-c0eac86b7041fe0a6a04d8a4ea9c01d0_1440w.jpeg
// 禁止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://www.reibang.com/p/4f8f36944a46
https://juejin.cn/post/6844903987632685063
https://blog.csdn.net/u010352770/article/details/101538528

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伞广,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子定血,更是在濱河造成了極大的恐慌赔癌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澜沟,死亡現(xiàn)場離奇詭異灾票,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)茫虽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人占调,你說我怎么就攤上這事∩吨剑” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵婴氮,是天一觀的道長斯棒。 經(jīng)常有香客問我,道長主经,這世上最難降的妖魔是什么荣暮? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮罩驻,結(jié)果婚禮上穗酥,老公的妹妹穿的比我還像新娘。我一直安慰自己惠遏,他們只是感情好砾跃,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著节吮,像睡著了一般抽高。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上透绩,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天厨内,我揣著相機(jī)與錄音,去河邊找鬼渺贤。 笑死,一個胖子當(dāng)著我的面吹牛请毛,可吹牛的內(nèi)容都是我干的志鞍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼方仿,長吁一口氣:“原來是場噩夢啊……” “哼固棚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仙蚜,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤此洲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后委粉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呜师,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年贾节,在試婚紗的時候發(fā)現(xiàn)自己被綠了汁汗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衷畦。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖知牌,靈堂內(nèi)的尸體忽然破棺而出祈争,到底是詐尸還是另有隱情,我是刑警寧澤角寸,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布菩混,位于F島的核電站,受9級特大地震影響扁藕,放射性物質(zhì)發(fā)生泄漏沮峡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一纹磺、第九天 我趴在偏房一處隱蔽的房頂上張望帖烘。 院中可真熱鬧,春花似錦橄杨、人聲如沸秘症。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乡摹。三九已至,卻和暖如春采转,著一層夾襖步出監(jiān)牢的瞬間聪廉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工故慈, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留板熊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓察绷,卻偏偏與公主長得像干签,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拆撼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容