Skip to main content

(譯)Vue.js App 效能優化: part1 – 效能優化和lazy loading

· 10 min read
Adam You

譯者說明

最近使用Vue.js投入公司產品開發花了不少心血,關於前端的效能是一件很重要的事情,這個系列文有做了一些有趣的探討,原文作者似乎也還沒寫完,我先從第一篇開始翻起,原文在此:

https://itnext.io/vue-js-app-performance-optimization-part-1-introduction-to-performance-optimization-and-lazy-29e4ff101019


當行動優先導向成為標準、並且不確定性的網路環境也成為我們必須考慮的因素時,讓應用程式保持高速成為越來越困難的事情。在這個系列當中我會挖掘更深的Vue優化技術 – 我們在Vue Storefront當中所使用的技術,而你也可以用在你的Vue.js應用程式當中來讓他們瞬間讀取並且表現得平滑。我的目的是在這系列中對於Vue.js應用程式效能給出一個完整的指南。

  • 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 – 預讀取

Webpack打包如何運作?

本系列中大部份的提示都是聚焦在使我們的js bundle更小。為了了解這是至關重要的,我們需要了解Webpack是如何把全部檔案打包來起的。

當Webpack打包所有資源時會產生依賴樹,這是一個連結所有我們使用import引入檔案的圖型。假設我們在Webpack config中使用main.js作為特定入口,他就會是整個依賴樹的根節點。那每一個import到該檔案底下的js module都會成為圖型當中的葉節點,並且每一個import進該葉節點的module也成為他的葉節點。

Wabpack使用這個依賴樹圖型偵測該納入打包輸出當中。打包出來會是一個包含所有依賴樹中的module的單一javascript檔案(或者多個,後面章節會寫)

我們可以把這個進程繪製成以下如圖:

現在我們知道打包是如何運作的,很明顯的他會成長到專案內最大化的初始javascript打包檔案。檔案愈大代表使用者需花更多時間下載,也代表使用使用者更可能離開我們的網站。

簡而言之,大部份的情況下愈大的打包=愈少的用戶。

##Lazy loading

所以為了改進我們的應用程式,當我們需要增加功能時該如何減少打包檔案的大小?當案很簡單-lazy loading和程式碼切割。

從字面上來看lazy loading就是懶惰得只載入應用程式的一部份。另外一種說法就是-只載入我們真的需要的部份。程式碼切割只切割出這個載入的部份。

大部份的情況下當使用者進入網站時不需要javascript打包中的所有程式,就算我們有3個不同的路由位置而使用者無論如何都需要下載、解析、並執行打包程式-不論他們是否會需要用到。這是多麼浪費時間和力氣!

Lazy loading允許我們切割打包並提供所需要的部份,使用者不需要浪費時間下載及解析他們不需要的程式。要看網站實際上使用到的javascript程式我們可以到開發者工具->cmd+shift+p ->輸入”coverage”->點擊”錄製”,接著我們應該可以看到下載了多少實際上使用到的程式。

每一個被標成紅色的就是目前路由裡不需要並可以被延遲載入的程式,如果你有用到source map那你可以點選清單裡任何沒有被引用到的檔案。從以上觀察可知甚至vuejs.org也有很大的進步空間^_^。

透過lazy loading特性的component和程式庫,我們砍掉了60%d Vue Storefront的打包檔案容量。

OK,我們現在知道lazy loading是什麼而且有多實用了^_^,現在我們來看他可以如何用在我們的Vue.js應用程式。

##Dynamic imports

透過webpack的dynamic imports可以很輕易的延遲載入部份程式,我們來看他是如何運作並且和一般的import有什麼不同。

如果我們使用一般的方式import JS module會像如下:

// main.js

import ModuleA from './module_a.js'

ModuleA.doStuff()

他會被加入到main.js的依賴樹中的葉節點並被打包進去。

但如果我們只有在特定情況下才會用到ModuleA呢(比如說使用者互動的事件)?在最一開始的時候就打包進去不是一個好主題。我們需要一個方式告訴我們的應用程式何時該下載這個部份的程式。

這也就是動態載入所能解決的問題!現在來看看以下例子:

//main.js

const getModuleA = () => import('./module_a.js')

// invoked as a response to some user interaction

getModuleA()

.then({ doStuff } => doStuff())

來快速看一下會發生什麼事:

我們寫一個function回傳import()函式以取代直接引入module_a.js。現在webpack動態import進來的部份程式,除非函式被呼叫否則程式不會被import並下載。之後這段程式將只會在特定的操作中被下載下來。

透過動態載入我們基本上隔離了特定的葉節點(在此例中為module_a),他會在我們決定載入的時候才加入依賴樹並下載(這暗示了我們同時也切割了module_a.js當中依賴的module)

我們來看另一個更好的例子來說明這個機制。假設我們有4個檔案:main.js、module_a.js、module_b.js、module_c.js,來了解動態載入是如何運作的,我們只需要main和module_a的原始碼:

//main.js

import ModuleB from './mobile_b.js'

const getModuleA = () => import('./module_a.js')

getModuleA()

.then({ doStuff } => doStuff()

)

//module_a.js

import ModuleC from './module_c.js'

把module_a作為一個動態載入用的module可以切割掉依賴樹中的module_a和他的子節點。當module_a動態載入時會連同裡面import的檔案一起載入。換句話說,我們可以建置了一個新的依賴圖的進入點。

這就是這個依賴圖和打包會長的樣子。

Lazy loading Vue components

我們現在知道lazy loading是什麼並且為何需要了,現在是時候來看我們可以如何在Vue應用程式當中使用。

好消息是,透過以上語法可以超級簡單的載入整個SFC(單文件Component-也就是.vue檔)、包含css和html。

const lazyComponent = () => import('Component.vue')

…只要這樣就搞定了!現在component只有在你呼叫他時才會載入。以下是最常使用動態載入Vue component的方法:

  • 把import寫成可呼叫的函式
const lazyComponent = () => import('Component.vue')

lazyComponent()
  • component被呼叫後渲染

請注意lazyComponent函式只有在呼叫他時才會渲染進template。

<template>
<div>
<lazy-component />
</div>
</template>
<script>
const lazyComponent = () => import('Component.vue')
export default {
components: { lazyComponent }
}
</script>

例如:

<lazy-component v-if="false">

這樣寫的話他是不會加入到dom當中的(但當v-if參數變為true時就會載入,這也是個不錯的方法)

結語

Lazy loading是一個使你的網站更有效減少容量的好方法,我們學到如何在Vue component當中使用lazy loading。下一個篇文章我會告訴你如何使用vue-router和動態路由減少你的Vue應用程式容量。