vue2 splitchunk分包優(yōu)化項(xiàng)目實(shí)戰(zhàn)&技術(shù)分享

1.目標(biāo)

結(jié)合vue2項(xiàng)目聊一下優(yōu)化的思路。主要聊一下webpack分包方向川队。
項(xiàng)目環(huán)境:Vue2 + webpack4
項(xiàng)目結(jié)構(gòu):pc和mobile集成在一個(gè)項(xiàng)目中呼奢,PC端:element-ui + vue(部分頁面會用到vant + jkUI)酷勺,Mobile: vant + vue + jkUI
分析工具:Chrome、webpack-bundle-analyzer插件

2.前置工作

安裝插件:

 npm i webpack-bundle-analyzer -D

配置腳本:

"analyze": "cross-env NODE_ENV=production ANALYZER=true vue-cli-service build"

修改配置(vue.config.js):

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
// ...省略部分代碼
// chainWebpack方式
config
  .when(process.env.ANALYZER == 'true', config => {
    config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin)
  })

// configureWebpack方式
process.env.ANALYZER == "true" && config.plugins.push(new BundleAnalyzerPlugin());

3.啟動項(xiàng)目分析資源加載

 npm run dev
圖1-f12查看network.png

簡單分析上圖膳凝,可以得出結(jié)論:
1.移動端加載了非必要資源:element-ui
2.vendors體積過大碑隆,導(dǎo)致后續(xù)加載阻塞
接下來先從這兩個(gè)方向分析bundle問題

4.項(xiàng)目bundle分析

npm run analyze

以cms項(xiàng)目代碼為例,執(zhí)行以上命令分析


圖2-初始bundle分布.png
圖3-bundle大小.png

結(jié)合之前的結(jié)論和上面的bundle分布圖蹬音,決定從下面幾個(gè)方向進(jìn)行優(yōu)化:
1.分離jk-ui組件庫
2.分離lodash庫
3.分離moment庫

5.開始優(yōu)化

webpack4中主要通過splitchunk來進(jìn)行分包操作

// 項(xiàng)目初始配置
splitChunks: {
  chunks: 'all',
  cacheGroups: {
    libs: {
      name: 'chunk-libs',
      test: /[\\/]node_modules[\\/]/,
      priority: 10,
      chunks: 'initial' // only package third parties that are initially dependent
    },
    elementUI: {
      name: 'chunk-elementUI',
      priority: 20, 
      test: /[\\/]node_modules[\\/]_?element-ui(.*)/, 
      enforce: true
    },
    echarts: {
      name: 'chunk-echarts', 
      priority: 12, 
      test: /[\\/]node_modules[\\/]_?echarts(.*)/
    },
    commons: {
      name: 'chunk-commons',
      test: resolve('src/components'), 
      minChunks: 3, 
      priority: 5,
      reuseExistingChunk: true
    }
  }
}

介紹一下all, initial, async(默認(rèn))區(qū)別:
all: 把動態(tài)和非動態(tài)模塊同時(shí)進(jìn)行優(yōu)化打包上煤;所有模塊都扔到 vendors.bundle.js 里面
initial: 把非動態(tài)模塊打包進(jìn) vendor,動態(tài)模塊優(yōu)化打包
async: 把動態(tài)模塊打包進(jìn) vendor著淆,非動態(tài)模塊保持原樣(不優(yōu)化)

結(jié)合圖2-bundle分布圖分析修改splitchunk配置:

// 修改后配置
splitChunks: {
  chunks: 'all',
  cacheGroups: {
    libs: {
      name: 'chunk-libs',
      test: /[\\/]node_modules[\\/]/,
      priority: 10,
      chunks: 'all'
    },
    elementUI: {
      priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
      test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
      reuseExistingChunk: true
    },
    jkUI: {
      priority: 20, 
      test: /[\\/]node_modules[\\/]_?jk-ui(.*)/,
      maxSize: 460800, // gzip 150kb
      minSize: 245760, 
      reuseExistingChunk: true
    },
    lodash: {
      priority: 20, 
      test: /[\\/]node_modules[\\/]_?lodash(.*)/,
      reuseExistingChunk: true
    },
    moment: {
      priority: 20, 
      test: /[\\/]node_modules[\\/]_?moment(.*)/,
      reuseExistingChunk: true
    }
  }
}
圖4-分包后bundle分布.png

圖5-分包后bundle大小.png

6.根據(jù)bundle分析代碼

對項(xiàng)目代碼進(jìn)行分析劫狠,可以看出一些明顯的問題:


圖6-lodash全局引入問題.png

圖7-moment全局引入問題.png

圖8-element-ui全局引入問題.png

1.lodash全局引入,改成按需加載
2.moment引入永部,可以采用day.js替換
3.element-ui是全局引入的独泞,包體積有點(diǎn)偏大,改成按需加載

7.進(jìn)一步優(yōu)化

1.將引入lodash的地方改為按需引入苔埋,或者改成utils自己封裝的方法

import { throttle } from 'lodash'
import { debounce } from 'lodash'
import { cloneDeep } from 'lodash'
// 改為
import throttle from 'lodash/throttle'
import debounce from 'lodash/debounce'
import cloneDeep from 'lodash/cloneDeep'
圖9-優(yōu)化后的lodash體積減小.png

重新運(yùn)行分析后發(fā)現(xiàn)lodash體積非常小懦砂,并且沒有單獨(dú)分離出來,可以刪除splitchunk中的lodash配置

2.將引入momentjs的地方,采用dayjs替換

// 去除moment引用并安裝dayjs
moment().format('YYYY-MM-DD')
moment(date).isValid()
// 改為
dayjs().format('YYYY-MM-DD')
dayjs(date).isValid()

重新運(yùn)行分析后momentjs包被換掉荞膘,總體體積減小罚随,可以刪除splitchunk中的moment配置


圖10-優(yōu)化moment使用后總體bundle體積減小.png

3.接下來優(yōu)化element-ui部分

在這之前我們先看一下jk-ui部分的加載問題


圖11-訪問mobile對應(yīng)的路由,加載了jk-ui組件庫.png
圖12-訪問pc下的cms-page-list路由羽资,也加載了jk-ui組件庫.png

顯然這個(gè)地方不是我們理想的情況淘菩,理想情況下應(yīng)該是移動端才會加載jk-ui組件庫,也就是按需加載屠升,只有用到的頁面才會加載對應(yīng)的庫潮改,分析一下原因,應(yīng)該是由于我們對于jk-ui配置的問題腹暖,當(dāng)我們不設(shè)置chunks時(shí)汇在,默認(rèn)繼承spilitChunks.chunks屬性,也就是all脏答,設(shè)置為all的chunks會被默認(rèn)加載趾疚,現(xiàn)在我們改為async重新運(yùn)行看看

jkUI: {
  priority: 20,
  test: /[\\/]node_modules[\\/]_?jk-ui(.*)/,
  maxSize: 460800,
  minSize: 245760,
  reuseExistingChunk: true,
  chunks: 'async' // 新增,之前為默認(rèn)繼承外層的all屬性
}

重新運(yùn)行后訪問兩個(gè)頁面路由:


圖13-重新訪問mobile路由以蕴,正常加載jk-ui組件庫資源.png
圖14-重新訪問pc端路由,jk-ui資源不再加載.png

通過上述實(shí)踐辛孵,當(dāng)動態(tài)加載的資源丛肮,設(shè)置成async之后被分離出來單獨(dú)的chunk,只會在用到的地方才加載魄缚,設(shè)置成all則不行宝与。

回到我們開始的話題,繼續(xù)優(yōu)化element-ui庫冶匹,從上述圖13中其實(shí)可以看到习劫,mobile路由加載了element資源,其實(shí)這并不是我們所希望的嚼隘,所以分兩個(gè)點(diǎn)來優(yōu)化element-ui诽里。(重點(diǎn):需要保證element-ui為動態(tài)加載,并且需要設(shè)置為async模式飞蛹。)
-第一個(gè)目標(biāo):優(yōu)化element-ui體積
-第二個(gè)目標(biāo):實(shí)現(xiàn)element-ui在pc端路由才加載

圖15-原element-ui加載代碼.png

查看項(xiàng)目代碼谤狡,可以看出element-ui是全局加載的,會導(dǎo)致element-ui包體積很大卧檐,這一點(diǎn)可以通過按需引入來解決問題墓懂。從之前的bundle分析中大概可以看到element-ui的體積大概為159kb(gziped)

由于element-ui的范圍較廣,選取一個(gè)pc頁面cms-page-list作為示例:

// 刪除main.js全局加載
if (!isMobile()) {
  // require('element-ui/lib/theme-chalk/index.css')
  // const ElementUI = require('element-ui')
  // Vue.use(ElementUI, { size: 'small' })
} else {
  const sensors = require('@/utils/sensors').default
  Vue.prototype.$sensors = sensors
}
// babel.config.js配置按需引入,參考下element官網(wǎng)
[
  'component',
  {
    libraryName: 'element-ui',
    styleLibraryName: 'theme-chalk'
  }
]
// 頁面級別按需引入element組件霉囚,頁面中子組件引用的element組件也需要按相同方式引入
import { Input, Button, Form, Select, DatePicker, Table, TableColumn } from 'element-ui'
// 省略...
components: {
  ElInput: Input,
  ElButton: Button,
  ElForm: Form,
  ElSelect: Select,
  ElDatePicker: DatePicker,
  ElTable: Table,
  ElTableColumn: TableColumn
}
// 刪除utils/decorator.js首行引用捕仔,否則會造成mobile頁面引用關(guān)系,由于mobile頁面中引用了decorator文件,會導(dǎo)致element被引入
import MessageBox from 'element-ui'
// 修改splitChunks中element-ui相關(guān)配置
elementUI: {
  priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
  test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
  reuseExistingChunk: true,
  chunks: 'async'  // 新增榜跌,之前為默認(rèn)繼承外層的all屬性
}

優(yōu)化后:


圖16-優(yōu)化element-ui后pc正常加載element-ui資源闪唆,且體積減小.png

圖17-優(yōu)化element-ui后mobile不加載element-ui資源.png

圖18-優(yōu)化后bundle分布圖.png

圖19-優(yōu)化后chunk體積減小.png

從上圖看出chunk-libs中有vant庫,可以采用同樣的方式斜做,結(jié)合按需加載和async模式抽離苞氮,防止在pc端頁面冗余加載vant。

// 新增
vant: {
  test: /[\\/]node_modules[\\/]vant[\\/]/,
  priority: 20,
  reuseExistingChunk: true,
  chunks: 'async'
}
圖20-分離vant后bundle體積.png

優(yōu)化后vant從chunk-libs中分離瓤逼,且在引用到的頁面中才會加載笼吟。


圖21-分離vant后mobile正常加載vant庫.png

圖22-分離vant后pc不加載vant庫.png

8.最終配置

splitChunks: {
  chunks: 'all',
  minChunks: 1,
  maxAsyncRequests: 30, // 最多30個(gè)請求
  maxInitialRequests: 30, // 最多首屏加載30個(gè)請求
  cacheGroups: {
    libs: {
      name: 'chunk-libs',
      test: /[\\/]node_modules[\\/]/,
      priority: 10
    },
    elementUI: {
      priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
      test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
      reuseExistingChunk: true,
      chunks: 'async'
    },
    jkUI: {
      priority: 20,
      test: /[\\/]node_modules[\\/]_?jk-ui(.*)/,
      maxSize: 460800,
      minSize: 245760,
      reuseExistingChunk: true,
      chunks: 'async'
    },
    vant: {
      test: /[\\/]node_modules[\\/]vant[\\/]/,
      priority: 100,
      reuseExistingChunk: true,
      chunks: 'async'
    }
}

9.總結(jié):

1.element-ui不能直接引入,否則無法在分包后達(dá)到最優(yōu)體積霸旗,直接import('element-ui')或者import ElementUI from 'element-ui'都會在最后打包生成chunk時(shí)生成包含element全量包贷帮,所以要采用頁面級別引入組件的方式來做按需引入。
2.入口文件main.js中也不能通過import { MessageBox } from 'element-ui'诱告,Vue.prototype.$message = MessageBox方式掛在到Vue的原型上撵枢,否則也會導(dǎo)致生成的chunk包含element整個(gè)包【樱可以在app.vue文件中掛載

import MessageBox from 'element-ui'
// 省略...
created() {
  Vue.prototype.$MessageBox = MessageBox
}

3.路由懶加載的頁面中锄禽,import xxx from 'xxx'可以看作動態(tài)導(dǎo)入。
4.import('xxx')為動態(tài)導(dǎo)入靴姿。

問題:
1.為什么vant會被分為多個(gè)chunk沃但?
2.分離chunk會額外生成一個(gè)css,如何合并佛吓?

拓展:
js新特性Import Maps:https://mp.weixin.qq.com/s/6KV1Q-7Wvwb-8E81fTooWA

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宵晚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子维雇,更是在濱河造成了極大的恐慌淤刃,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吱型,死亡現(xiàn)場離奇詭異逸贾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)唁影,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門耕陷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人据沈,你說我怎么就攤上這事哟沫。” “怎么了锌介?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵嗜诀,是天一觀的道長猾警。 經(jīng)常有香客問我,道長隆敢,這世上最難降的妖魔是什么发皿? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮拂蝎,結(jié)果婚禮上穴墅,老公的妹妹穿的比我還像新娘。我一直安慰自己温自,他們只是感情好玄货,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悼泌,像睡著了一般松捉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馆里,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天隘世,我揣著相機(jī)與錄音,去河邊找鬼鸠踪。 笑死丙者,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的营密。 我是一名探鬼主播蔓钟,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼卵贱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起侣集,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤键俱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后世分,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體编振,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年臭埋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了踪央。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瓢阴,死狀恐怖畅蹂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荣恐,我是刑警寧澤液斜,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布累贤,位于F島的核電站,受9級特大地震影響少漆,放射性物質(zhì)發(fā)生泄漏臼膏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一示损、第九天 我趴在偏房一處隱蔽的房頂上張望渗磅。 院中可真熱鬧,春花似錦检访、人聲如沸始鱼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽风响。三九已至,卻和暖如春丹禀,著一層夾襖步出監(jiān)牢的瞬間状勤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工双泪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留持搜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓焙矛,卻偏偏與公主長得像葫盼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子村斟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

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