[Vue] 跟著 Vue 闖蕩前端世界 - 15 執行檔案瘦身計畫

  • 1404
  • 0
  • Vue
  • 2019-07-25

針對第三方套件使用的合宜性來進行審視,並透過 async component 與 webpack 代碼分割功能來實現 Lazy Loading Routes 機制,讓打包後產出的 bundle 檔案能依照各頁面需求載入所需的代碼。

前言


在網站開發過程中,通常被關注的點只會在功能正確性上,但隨著日子逼近上線期,漸漸地就會開始關注透過 webpack 打包出來的那包檔案,並且在意「進入網站」所需載入的檔案大小及時間,此時檔案大小就會成為錙銖必較的議題。筆者整理了一些近期專案的瘦身心得,以下說明。

vue-cli: v2.9.2
vue: v2.5.2
vue-router: 3.0.1
webpack: 3.6.0

 

檢查 bundle 檔案內含物


在預設情況下,絕大部分的程式碼透過 webpack 打包後的會存在以下兩隻 bundle 檔中:

  • vendor.[hash].js : 第三方套件
  • app.[hash].js : 大部分開發的代碼都於此

 

要解決檔案過大的問題,首先必須知道這些 bundle 檔究竟包含了那些代碼模組,這樣我們才可以對此進行分析及拆解處理;若是使用 vue-cli 建立的專案,可直接加入 --report 參數來產出這個分析檔案。 

 

在打包的過程中主要是利用 webpack-bundle-analyzer 這個插件來分析各個 bundle 檔案;因此若不是透過 vue-cli 產出專案架構的朋友,也可以直接加入此套件進行分析即可。

 

建置後就可以看到所有 bundle 檔案各自的內含物,區塊越大表示檔案越大,因此可由此審視過大的組件是否有其存在的必要;另外,也可以做為瘦身計畫的成果表,由此顯示圖形化的成果是否有如預期變化 。

 

 

清查使用套件


由於第三方套件在打包後都會放置在 vendor.[hash].js 中,所以稍有套件濫用的情況就會造成該檔案異常腫大,因此這隻 bundle 檔會是我們關注的重點項目之一;透過 bundle analyzer 產出的區塊圖可以很清楚看出檔案相對大的套件項目(因為字特大特清楚),這時可以考慮的方向如下,後續會針對各情境進行說明。

  1. 是否真有使用到這個套件
  2. 套件是否包含無用的檔案可移除 (ex. 各國語系檔)
  3. 套件是否只在少數頁面使用,可作動態載入 (有需要在用)
  4. 套件是否能夠僅載入需要的部分就好,不要整個都載入

 

似乎沒使用到這個套件

在開發期間,會因為特殊需求會載入相關的套件來加速開發,但大家都知道需求的變動是很快的,有時候會有需求已被移除但忘記移除套件的情況發生,就會造成無用套件仍然被包進程式中,因此藉這個機會清查一下套件清單也不錯。

 

套件存在無用的語系檔案 - moment.js

從分析圖可以清楚發現在 vendor.[hash].js 中 moment 套件中被放入各國的語系文字,光是 moment 套件就佔了 221.08 KB 大小,而事實上我們只需要加入系統需支援的語系即可,因此若可以移除其他不須的語系檔應可大幅減少 bundle 檔案的大小。

我們可在 webpack 使用 ContextReplacementPlugin 來設定 moment 只需要 en 及 zh-tw 語系即可。

調整後的檔案已經從 221.08 KB 減為 50.52 KB ,足足瘦身約 171 KB 左右,是不是相當有感!

 

只有特定頁面才需要的套件 - highcharts.js

先前有提到第三方套件都會放置在 vendor.[hash].js 中,也就是表示進入網站就會載入這隻 bundle 檔案,但若特定套件如 highcharts 只在少數頁面才會用到,而其他頁面也需花時間載入這個沒用到的套件,這樣不是很不符經濟效益嗎?

這時我們應將 highchars 從 bundle 檔移出,透過 webpack 設定將這套件切出 highcharts.[hash].js 獨立的 chunk 檔案,讓有需要的頁面才去下載,這樣才是比較合理的作法。

這樣又讓 vendor.[hash].js 瘦身 221.48 KB,讓進入網站的客戶少花一點時間載入不必要的檔案。

 

只單獨載入所需模組 - lodash.js

在使用一些 Functional Library 的時候可以特別注意一下 import 方式,是否可以只 import 所需 Function 就好,避免將整個 Library 都載入而造成 vendor.[hash].js 腫大。以 lodash 為例,大家可以考慮 one-by-one 的方式來只 import 所需 function 會比較好喔!

// X: Import the whole library
import _ from 'lodash'

// O: Import specific methods one by one
import map from 'lodash/map'

 

 

路由頁面組件分組


當網站開發越多功能時,可以發現 app.[hash].js 逐漸加大,因為預設會將網站中非套件類的代碼都打包至 app.[hash].js 這個 bundle 檔案,這也就表示只要訪問站台,就必須要把整站所有代碼都載下來,這樣會花費許多不必要的等待時間;因此我們可以透過 vue 的 async component 搭配 webpack 設定來拆出各頁面所需的獨立 bundle 檔案,只有在訪問到該頁面時才需載入,實現 Lazy Loading Routes 機制。

以下是一個開發中的專案,在打包後產出 app.[hash].js 約為 2MB,這代表用戶在初次訪問時就必須花費時間等待下載 2MB 檔案只為了顯示一個畫面;而且這只是開發中的專案,如果全部功能都完成後,也許會來到 4MB 的大小,所以必須拆解 app.[hash].js 中的代碼,讓訪問頁面時才載入該頁面所需的代碼。

 

在 router 中可用 dynamic import 載入各頁面組件,並可依據功能名稱來 group 相關頁面組件在相同 chunk 中,如果打包出來的 chunk 檔仍然過大,也可再考慮細拆讓特定頁面組件自己存在一個 chunk 也行。

 

當 chunk 切得過細,就又要考慮到是否有重複程式碼不斷出現在各 chunk 中,一直載一樣的東西浪費網路流量;此時可考慮設定 webpack.optimize.CommonsChunkPluginminChuncks 來定義有多少重複份數代碼散在各 chunk 時需要再抽出成為一個共用 vendor-async.[hash].js 檔案。不過這是雙面刃,如果將該值設太大,表示只有少數組件共用次數超過,所以還是會有重複下載的流量浪費;如果設太小,等於共用兩次的東西就包到共用區,有可能剛進網站根本沒用到該功能就要載入了。

 

接著調整 Webpack 設定,讓 bundle 檔案名稱具有意義。

 

完成上述調整後 app.[hash].js 已從 2MB 降到 592 KB,減少約 1.4 MB 無謂的資料下載時間;另外當訪問到特定功能如 mobile 相關功能頁面時,也只需要再加載 mobile.[hash].js 就可以,這樣可以避免不必要的資源一次全部載入。

 

 

參考資訊


Lazy Loading Routes

Dynamic & Async Components

Optimize your libraries with webpack

 

 


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !