vue-cli3 webpack 打包優(yōu)化實(shí)踐總結(jié)

年前做過(guò)一個(gè)公司內(nèi)部的后管平臺(tái)在辆,前端使用 vue.js 框架開(kāi)發(fā)规揪,基于 vue-cli3 腳手架構(gòu)建項(xiàng)目,ui 組件庫(kù)使用 Element-UI乍赫,其他組件還包括 axios瓣蛀,echarts,vue-router雷厂,vuex 等惋增,該項(xiàng)目功能簡(jiǎn)單,涉及頁(yè)面 40 個(gè)改鲫,都是簡(jiǎn)單的表單或者列表頁(yè)器腋。上線的時(shí)候直接 npm run build 命令打包,未在 vue.config.js 中做任何配置钩杰,將 dist 打好的包放到 nginx 服務(wù)器,做個(gè)反向代理就用上了诊县。

未做優(yōu)化的打包結(jié)果如下:


未做路由組件動(dòng)態(tài)引入

可以看出 chunk-vendors.82702712.js 文件大小有 1M 多讲弄,對(duì)應(yīng)的 css 文件也有 194 kb,app.653b22c5.js 有 172 kb依痊,項(xiàng)目中所有的三方庫(kù)都打包到 chunk-vendors.82702712.js 中避除,而所有的頁(yè)面,自定義組件等都在 app.653b22c5.js 中胸嘁。因?yàn)闆](méi)有使用動(dòng)態(tài)加載瓶摆,所以這些 1M,幾百 kb 的 js 都會(huì)在剛訪問(wèn)網(wǎng)站的時(shí)候一起加載性宏,pc 上還好群井,一旦放到移動(dòng)端,弱網(wǎng)情況下只能給用戶展示一個(gè)大白板毫胜。

訪問(wèn)項(xiàng)目主頁(yè)书斜,通過(guò) Chrome 的 Instrument converge 功能查看 js,css 的資源使用率酵使,發(fā)現(xiàn) chunk-vendors.82702712.js 的未使用率達(dá)到 85.4%荐吉,對(duì)應(yīng)的 chunk-vendors.ea3fa8e3.css 未使用率 98.3%,app.js 的未使用率是 88.4 %口渔,app.css 未使用率是 85%样屠,說(shuō)明項(xiàng)目首頁(yè)訪問(wèn)的資源體積大,是因?yàn)榘艘淮蟛糠治词褂玫慕M件,如果每個(gè)頁(yè)面都只加載自己需要的組件痪欲,那網(wǎng)站的訪問(wèn)速度會(huì)得到較大的提升悦穿。

資源使用狀況

基于上面遇到的問(wèn)題,優(yōu)化打包可以從以下幾個(gè)方面入手勤揩。

  1. 組件動(dòng)態(tài)引入咧党,按需加載,app.653b22c5.js 可以拆分成若干個(gè) js陨亡,使用時(shí)才去加載對(duì)應(yīng)的 js傍衡。
  2. 三方庫(kù)組件,按需加載负蠕,chunk-vendors.82702712.js 可以做拆分蛙埂,不必一下全部加載,可在使用時(shí)再去加載對(duì)應(yīng)部分的 js遮糖。
  3. 對(duì)資源文件做壓縮绣的,上圖中可以看到 1069.96kb 的文件, gzip 可以壓縮到 310.03kb欲账。

著手優(yōu)化項(xiàng)目打包

路由懶加載

結(jié)合 Vue 的異步組件和 Webpack 的代碼分割功能屡江,輕松實(shí)現(xiàn)路由組件的懶加載。
在 router.js 中赛不,原來(lái)導(dǎo)入組件的方式是:

import Home from './views/Home.vue'
import Login from './views/Login.vue'
import Role from './views/system/Role.vue'
import User from './views/system/User.vue'
...

這就導(dǎo)致這些組件最后都打包到一個(gè) app.653b22c5.js 文件中惩嘉,我們修改導(dǎo)入組件的方式,改為動(dòng)態(tài)導(dǎo)入:

const Home = () => import('./views/Home.vue')
const Login = () => import('./views/Login.vue')
...

修改完所有的組件導(dǎo)入后踢故,在運(yùn)行 npm run build 打包發(fā)現(xiàn)文黎,原來(lái)的 app.653b22c5.js 和 css 文件得到了拆分。

路由動(dòng)態(tài)加載-1

可以看到 app.js 文件從原來(lái)的 172.77kb 降到了 18.14 kb殿较。

路由動(dòng)態(tài)加載-2

但是拆分過(guò)細(xì)耸峭,每個(gè)頁(yè)面都獨(dú)自拆分出來(lái),一個(gè) js 大一點(diǎn)的 7kb淋纲,小的才 2劳闹,3kb,css 拆分后有的 0.1kb洽瞬,甚至還有 0.03kb的玷或,拆分的粒度過(guò)細(xì),也會(huì)造成更多的網(wǎng)絡(luò)資源請(qǐng)求片任,對(duì)網(wǎng)站加載造成影響偏友。

其實(shí)我們的一個(gè)功能流程通常會(huì)涉及多個(gè)頁(yè)面,如果能將多個(gè)頁(yè)面組件分組打包对供,效果會(huì)更好位他,避免了多次網(wǎng)絡(luò)資源請(qǐng)求氛濒。

路由懶加載分組

分組修改方法如下:

const Role = () => import(/* webpackChunkName: "group-role" */'./views/system/Role.vue')
const AddRole = () => import(/* webpackChunkName: "group-role" */'./views/system/AddRole.vue')
const EditRole = () => import(/* webpackChunkName: "group-role" */'./views/system/EditRole.vue')

const User = () => import(/* webpackChunkName: "group-user" */'./views/system/User.vue')
const AddUser = () => import(/* webpackChunkName: "group-user" */'./views/system/AddUser.vue')
const EditUser = () => import(/* webpackChunkName: "group-user" */'./views/system/EditUser.vue')
const UserAllotRole = () => import(/* webpackChunkName: "group-user" */'./views/system/UserAllotRole.vue')

按照功能將組件分組后,打包結(jié)果如下:

路由組件動(dòng)態(tài)引入模塊分組

每個(gè)功能模塊打包后的 js 大概有十幾kb鹅髓,文件數(shù)量也大大減少舞竿。

node_modules 打包

chunk-vendors.js 是對(duì) node_modules 中三方庫(kù)的打包,本項(xiàng)目中 chunk-vendors.js 的大小窿冯,Element-UI 占了大部分比例骗奖。group-monitor.js 為 327.92 kb 是因?yàn)槠湟肓?echart。

我在 main.js 中完整引入了 Element-UI醒串,而不是按需引入:

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import http from './utils/network/http'

Vue.use(ElementUI)

所以會(huì)將 Element-UI 的所有組件打包到項(xiàng)目中执桌。

對(duì)三方庫(kù)的引用務(wù)必使用按需加載,不需要的組件就不要引入進(jìn)來(lái)芜赌。優(yōu)化時(shí)將 Element-UI 常用的組件在 main.js 中按需引入仰挣。

import { Container, Menu } from 'element-ui';

Vue.use(Container);
Vue.use(Menu);

頁(yè)面中有單獨(dú)用到的 Element-UI 組件,則在該頁(yè)面中單獨(dú)引入對(duì)應(yīng)的組件缠沈,這樣在訪問(wèn)該頁(yè)面時(shí)才去加載這些 ui 組件膘壶。

由于 40 多個(gè)頁(yè)面用到了 Element-UI 組件,改造工作量比較大洲愤,我用 echart 在其他頁(yè)面做測(cè)試颓芭,看下打包情況。

我找到三個(gè)頁(yè)面分別按需引入 echart.js:

// MonitorDetail.vue 按需引入 echart.js
var echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/pie');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
require('echarts/lib/component/legendScroll');

// Bulletin.vue 按需引入 echart.js
var echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/bar');
require("echarts/lib/chart/tree");

// About.vue 按需引入 echart.js
var echarts = require('echarts/lib/echarts');
// 與 Bulletin.vue 公共的引入
require('echarts/lib/chart/bar');
require("echarts/lib/chart/tree");
// 與 MonitorDetail.vue 公共的引入
require('echarts/lib/chart/pie');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
require('echarts/lib/component/legendScroll');
// 自己獨(dú)有的引入
require("echarts/lib/chart/treemap");
require("echarts/lib/chart/graph");
require("echarts/lib/chart/gauge");

About.vue 引入的 echart 組件中有一部分與 MonitorDetail.vue 相同柬赐,一部分與 Bulletin.vue 相同畜伐,還有三個(gè)頁(yè)面公共的部分,我們期望公共的部分打包成一個(gè) js躺率,不會(huì)重復(fù)打包,About.vue 獨(dú)有部分還和 Acount.vue 在一起打包万矾。

打包結(jié)果如下:

三方庫(kù)打包優(yōu)化

about~group-bulletin~group-monitor.js 是三個(gè)頁(yè)面 echart 的公共部分悼吱,about~group-monitor.js 是 About.vue 與 MonitorDetail.vue echart 的公共部分,about~group-bulletin.js 是 About.vue 與 Bulletin.vue 的公共部分良狈,about.js 獨(dú)有的 echart 組件則還在自己的 js 中后添。

只要我們按需引入三方庫(kù)組件,vue-cli3 就能智能的幫助我們合理的打包薪丁,這主要是依靠插件 SplitChunksPlugin 來(lái)完成的遇西。

gzip 壓縮

如果 Nginx 服務(wù)器開(kāi)啟 gzip,會(huì)將靜態(tài)資源在服務(wù)端進(jìn)行壓縮严嗜,壓縮包傳輸給瀏覽器后粱檀,瀏覽器再進(jìn)行解壓使用,這大大提高了網(wǎng)絡(luò)傳輸?shù)男事绕鋵?duì) js茄蚯,css 這類(lèi)文本的壓縮压彭,效果很明顯。

以下是 Nginx 開(kāi)啟 gzip 的配置:

# 開(kāi)啟|關(guān)閉 gzip渗常。
gzip on|off;

# 文件大于指定 size 才壓縮壮不,以 kb 為單位。
gzip_min_length 10;

# 壓縮級(jí)別皱碘,1-9询一,值越大壓縮比越大,但更加占用 CPU癌椿,且壓縮效率越來(lái)越低健蕊。
gzip_comp_level 2;

# 壓縮的文件類(lèi)型。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;

# 開(kāi)啟后如果能找到 .gz 文件如失,直接返回該文件绊诲,不會(huì)啟用服務(wù)端壓縮。
gzip_static on|off
    
# 是否添加響應(yīng)頭 Vary: Accept-Encoding 建議開(kāi)啟褪贵。
gzip_vary on;

# 請(qǐng)求壓縮的緩沖區(qū)數(shù)量和大小掂之,以 4k 為單位,32 為倍數(shù)脆丁。
gzip_buffers 32 4K;

如果 Nginx 沒(méi)有開(kāi)啟 gzip世舰,前端在打包的時(shí)候可以打包出一份資源的壓縮版本,Nginx 也會(huì)把壓縮文件傳輸給瀏覽器槽卫。

首先安裝一個(gè)插件:

npm i -D compression-webpack-plugin

在 vue.config.js 中配置下這個(gè)插件:

const CompressionPlugin = require("compression-webpack-plugin")

module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      return {
        plugins: [
          new CompressionPlugin({
            test: /\.js$|\.html$|\.css/,
            threshold: 10240,
            deleteOriginalAssets: false
          })
        ]
      }
    }
  }
}

nginx 服務(wù)器還要做一下簡(jiǎn)單配置:

gzip_static on;

使用 compression-webpack-plugin 插件后的打包結(jié)果如下:

使用 gzip 壓縮打包

上圖中的 .gz 就是對(duì)應(yīng)一個(gè)資源文件的壓縮版本跟压。

配置成功后,重新將代碼部署到 nginx歼培,重新載入 nginx 配置震蒋。

我們先看一下沒(méi)有開(kāi)啟 gzip_static off; gzip 的訪問(wèn)情況:

nginx 未開(kāi)啟 gzip 壓縮

我們直接看最大的兩個(gè)文件 chunk-vendors.js 和 chunk-vendors.css,他們的大小分別是 748kb 和 194kb躲庄,加載用時(shí)分別是 622ms 和 247ms查剖。

然后我們開(kāi)啟 gzip gzip_static on; 再看下資源的加載情況:

nginx 開(kāi)啟 gzip 壓縮

同樣是 chunk-vendors.js 和 chunk-vendors.css,他們的大小變成 190kb 和 29.3kb噪窘,加載時(shí)間變成 245ms 和 46ms笋庄。

以上就是使用 gzip 的效果。

Vuex 動(dòng)態(tài)注冊(cè)模塊

vuex 通常使用靜態(tài)模塊倔监,這些模塊都會(huì)打包到 app.js 中直砂,但是如果有的模塊過(guò)大而且不是立刻就會(huì)用到,我們可以動(dòng)態(tài)的注冊(cè)模塊到 vuex 中浩习。

在使用 vuex 某個(gè)模塊的時(shí)候才注冊(cè):

mounted () { 
    this.$store.registerModule('myModule', MyModule)
}

在不使用的時(shí)候静暂,注銷(xiāo)模塊:

beforeDestroy () {
    this.$store.unregisterModule('myModule')
}

這樣實(shí)現(xiàn)的效果是該頁(yè)面在加載時(shí)才下載模塊內(nèi)容,而不是剛訪問(wèn)網(wǎng)站就去下載谱秽。

工具

打包時(shí)籍嘹,我們可以通過(guò)一些輔助工具幫助分析打包情況闪盔,有重點(diǎn)的去做優(yōu)化。

打開(kāi) Chrome 的調(diào)試模式辱士,CMD+SHIFT+P 調(diào)出命令面板泪掀,輸入 Coverage,選擇 Show Coverage颂碘,然后訪問(wèn)指定網(wǎng)站异赫,點(diǎn)擊按鈕 Instrument converge 就能查看已加載資源的未使用率。

Instrument converge

使用 webpack-bundle-analyzer 插件可以分析出打包后的文件結(jié)構(gòu)头岔。

安裝該插件 npm i –D webpack-bundle-analyzer塔拳,vue.config.js 中做如下配置:

  chainWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      if (process.env.npm_config_report) {
        config
          .plugin('webpack-bundle-analyzer')
          .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
          .end()
        config.plugins.delete('prefetch')
      }
    }
  }

然后使用 npm run build --report 命令打包就可以查看到打包后的文件結(jié)構(gòu)。

webpack-bundle-analyzer

這個(gè)打包后的文件結(jié)構(gòu)頁(yè)面很有參考價(jià)值峡竣,對(duì)打包的優(yōu)化幫助很大靠抑。

參考文章

提升90%加載速度——vuecli下的首屏性能優(yōu)化
Vue 性能優(yōu)化:如何實(shí)現(xiàn)延遲加載和代碼拆分?
vue-cli3+nginx配置gzip壓縮

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末适掰,一起剝皮案震驚了整個(gè)濱河市颂碧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌类浪,老刑警劉巖载城,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異费就,居然都是意外死亡诉瓦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)力细,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)睬澡,“玉大人,你說(shuō)我怎么就攤上這事眠蚂∩反希” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵河狐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我瑟捣,道長(zhǎng)馋艺,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任迈套,我火速辦了婚禮捐祠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桑李。我一直安慰自己踱蛀,他們只是感情好窿给,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著率拒,像睡著了一般崩泡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猬膨,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天角撞,我揣著相機(jī)與錄音,去河邊找鬼勃痴。 笑死谒所,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沛申。 我是一名探鬼主播劣领,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼铁材!你這毒婦竟也來(lái)了尖淘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衫贬,失蹤者是張志新(化名)和其女友劉穎德澈,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體固惯,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梆造,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了葬毫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镇辉。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贴捡,靈堂內(nèi)的尸體忽然破棺而出忽肛,到底是詐尸還是另有隱情,我是刑警寧澤烂斋,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布屹逛,位于F島的核電站,受9級(jí)特大地震影響汛骂,放射性物質(zhì)發(fā)生泄漏罕模。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一帘瞭、第九天 我趴在偏房一處隱蔽的房頂上張望淑掌。 院中可真熱鬧,春花似錦蝶念、人聲如沸抛腕。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)担敌。三九已至摔敛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柄错,已是汗流浹背舷夺。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留售貌,地道東北人给猾。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像颂跨,于是被迫代替她去往敵國(guó)和親敢伸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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