在前一篇文章當中我們學到了什麼是切割程式碼、它是如何運作的以及在Vue.js應用程式中如何和lazy loading一起使用。現在我們將稍微深入一點的來看程式碼,並學習在Vue.js應用程式中最實用的程式碼切割的模式。
這個系列是基於從Vue Storefront的效能優化處理中所學到的。透過以下的技術我們可以砍掉我們最初的打包的70%,使他載入在一眨間就完成。
- Part 1 – 介紹效能優化及lazy loading
- Part 2 – Lazy loading路由及第三方庫的反向模式(anti-pattern)
- Part 3 – Lazy loading Vuex模組
- Part 4 – Lazy loading單一Component
- Part 5 – Lazy loading函式庫和尋找最小集合
- Part 6 – 使用Service Worker cache
- Part 7 – 預讀取
成長中的應用程式的問題
Vue-router是一個可以切割我們的網頁應用程式成為各別頁面。各自的頁面對應到特定的URL路徑。
有了這個想像之後我們有以下一個簡單的作品集程式架構:
你可能會注意到我們還不需要app.js打包中依賴的Home.vue和About.vue(以及其依賴的lodash)、並且無論使用者訪問哪個路由都會把他們下載下來。這很浪費下載流量而且浪費時間!
如果只是多下載一個程式這沒什麼大不了的,但你可以想如果這個程式成長得愈來愈大,每一個額外增長代表在初次訪問時需下載更大的打包。
就算只有1秒的時間是足以讓使用者分心並有可能離開我們的網站,這是不能夠被接受的!
圖:關於性能及人類感知的演講投影片 (Ilya Grigorik) https://www.youtube.com/watch?v=7ubJzEi3HuA
##使用vue-router以路由為基準的程式碼切割
為解決決這個情況 ,我們只需要使用前一篇文章學到的import語法、根據每個路由來切割打包。
就像Vue.js裡其他的任何東西 – 超級簡單。要取代掉直接地import component到路由物件裡,我們只需要貼上動態import的function就可以了。Component只有在路由要對應過去時才會被下載下來。
所以取代像這樣子直接載入路由component:
import RouteComponent form './RouteComponent.vue'
const routes = [
{ path: /foo', component: RouteComponent }
]
我們需要在這個路由上的入口處加入能產生新的打包的動態import。
const routes = [
{ path: /foo', component: () => import('./RouteComponent.vue') }
]
來看這個動態import的打包和路由長什麼樣子:
- app.js – 在每個路由中都需要的主要的app入口處打包(main.js)及函式庫/component。
- home.js – 首頁的打包,只有在路由進入時才會下載下來。
- about.js – 「關於」頁面(及他的依賴lodash),只有在路由進入時才會下載下來。
*打包名稱並不是戴正由webpack產生出來的而是為了方便理解而已。Webpack實際產生出來的名稱會像是0js 1.js,要依據你的webpack config設定而定。
這個技巧對幾乎所有的應用程式都是足以應付、都能有非常好的效果。
基於路由的程式碼切割在許多場合都能夠解決所有你的效能問題,幾乎所有的應用程式都能使用而且只花不到幾分鐘的時間就能實作!
##Vue生態體系中的程式碼切割
你可能會使用Nuxt或vue-cli來建置你的應用程式。這種情況就需要知道這兩者關於程式碼切割的自訂行為。
- 在vue-cli 3 所有的lazy loading模塊都預設是預加載(prefetch)的。我們稍後會學習預加載的使用。如果你希望知道更多關於vue-cli的預加載可以參考這邊。
- 在Nuxt 如果使用Nuxt的路由系統,則所有的頁面路由都是程式切割的開箱即用。
現在來看看非常給力且常見的反向模式(anti-pattern)可以帶給你的路由為基準的程式碼切割更厲害一些。
##函式庫打包反向模式
函式庫打包(vendor bundle)非常常見使用node_modules把全部各自獨立的js檔案全部納入。
僅管這個方法把全部的依賴放進同一個地方很吸引人,但這依然有綁住所有路由在一起的問題。
你看到問題了嗎?僅管我們只有在其中一個路由當中需要lodash(是當中的依賴),vendor.js綁定了全部的依賴所以無論如果他一定會被下載。
在同一個檔案綁定全部的依賴聽起來很誘人但也會導致你的應用程式會一次讀取全部的檔案。我們有更好的方法!
維持我們剛剛使用基於路由的程式碼切割足以確保只有需要時才下載,但這會導致一些程式碼的重覆。
我們假設Home.vue也需要lodash。
這個情況下從 /aboug (About.vue) 導向到 / (Home.vue) 會導致lodash下載了兩次。
這仍然比下載了一堆多餘的程式碼還要來的好,但如果我們已經有了這些依賴而不重覆利用是很沒意義的沒錯吧?
這也就是webpack splitChunksPlugin也以幫助我們的地方。只要在webpack config當中增加幾行,我們就可以把分散的打包中常用的依賴給聚起來,使他們可以被分享。
// webpack.config.js
optimization: {
splitChunks: {
chunks: 'all'
}
}
在模塊的屬性中我們只要告訴webpack哪個模塊程式應該被優化。設定這個屬性為all表示他應該優化所有的模塊。
你可以閱讀更多相關的處理 webpack docs
結語
透過路由切割你的程式是其中之一最棒(也最簡單)的方法來維持初始下載打包小的容量。下一個部份我們會學習關於其他較細的部份(Vuex stores以及獨立的component),從主要的打包中砍掉並延遲載入。