在前一篇文章中,我們學習了什麼是代碼分割,它是如何與 Webpack 一起工作的,以及如何在Vue應用程序中使用延遲加載。現在,我們將更深入地研究,並學習用於分割 Vue.js 應用程序最實用的模式。
本系列文章基於對 Vue Storefront 性能優化過程的學習。通過使用下面的技術,我們能夠將初始文件的大小減少70%,並在眨眼間使其加載。
- Part 1 — Introduction to performance optimization and lazy loading.
- Part 2 — Lazy loading routes and vendor bundle anti-pattern.
- Part 3 — Lazy loading Vuex modules
- Part 4 — Delivering good waiting experience and lazy loading individual components — soon
- Part 5 — Lazy loading libs and finding smaller equivalents — soon
- Part 6 — Performance-friendly usage of UI libraries
- Part 7 — Making use of Service Worker cache — soon
- Part 8 — Prefetching
應用程序增長的問題
Vue-router 是一個庫,它允許將我們的web應用程序自然地分割成單獨的頁面。每個頁面都是與某個特定URL路徑相關聯的路由。
瞭解了這一點,假設我們有一個簡單的應用程序,其結構如下:
整個JS代碼打包在一個文件中 - app.js
你可能已經注意到,我們正在訪問的路徑,可能不需要 Home.vue 或 About.vue(和它依賴的lodash),但它們都在相同的 app.js 文件中,無論用戶訪問哪一個路徑,都會被下載。多麼浪費下載和解析時間啊!
如果我們下載一個額外的路由,這並不是什麼大不了的事,但你可以想象這個應用會變得越來越大,任何新的增加將意味着在首次訪問時要下載更大的文件。
僅僅一秒鐘的時間足以讓用戶心理髮生變化,並很有潛在可能會離開我們的網站,這是不能接受的。
來自 Ilya Grigorik 關於性能和人類感知的精彩演講
以上圖片中出現的英文譯文:
- Wetware perception reaction times 人腦感知反應時間
- instant 立刻
- slight perceptible delay 輕微可察覺的延遲
- Task focus,perceptible delay 明顯的延遲
- Mental context switch 心理髮生變化
- I'll come back later 我一會兒再來
基於路由的代碼分割
爲了避免讓應用程序變得更糟,我們只需要使用上一篇文章中學習的動態導入語法爲每個路由創建單獨的包。
像在 Vue.js 中很容易實現延遲加載組件一樣,我們要做的不是直接將組件導入到路由對象中,而是傳遞一個 動態導入函數,只有在訪問這個路由時,纔會下載對應的路由組件。
所以不要像這樣靜態導入路由組件:
import RouteComponent form './RouteComponent.vue'
const routes = [{ path: /foo', component: RouteComponent }]
我們需要動態導入它,這將創建一個新的包,並將此路由作爲入口點:
const routes = [
{ path: /foo', component: () => import('./RouteComponent.vue') }
]
知道了這些,讓我們看看動態導入後打包的文件和路由是什麼樣子的:
使用這個設置,webpack 將創建三個包:
- app.js 我們的主文件,包含了應用程序入口點(main.js)和每個路由所需要的庫/組件。
- home.js/** 路徑時才被下載。
- about.js/about** 路徑時纔會被下載。
_爲了便於理解,文件名稱並不是由webpack生成的真實名稱。實際上是根據webpack配置生成 0.js1.js** 等_,具體取決於您的webpack配置。
這種技術幾乎適用於所有的應用,並且可以產生非常好的效果。
在許多情況下,基於路由的代碼分割將解決所有的性能問題,並且可以在不到幾分鐘的時間內應用到幾乎任何應用程序中!
Vue生態系統中的代碼拆分
你可能正在使用 Nuxt 或 vue-cli 來創建你的應用程序。如果是這樣,重要的是要知道它們都有關於代碼拆分的一些自定義行爲:
- 在 vue-cli3prefetchingvue-cliprefetching** 的知識,請看這篇文章。
- 在 Nuxt路由系統,所有頁面路由的代碼分割都是開箱即用的。
現在讓我們來看看非常流行且常被用到的反模式(anti-pattern),它可以降低基於路由的代碼分割的影響。
Vendor bundle 反模式
Vendor bundle 通常用於包含 node_modules 中的所有模塊的單獨js文件。
雖然把所有文件都打包在一起,以便將所有依賴關係保存在一個地方並緩存它們可能很吸引人,但這種方法引入了我們將所有路由打包在一起時遇到的同樣的問題:
你看到問題了嗎?即使我們只需要在一個路由中使用 lodash(它是其中一個依賴項),它就會被打包在 vendor.js 中以及所有其他依賴項中,因此它始終將會被下載。
將所有依賴項打包在一個文件中聽起來很吸引人,但這會延長應用程序的加載時間。我們可以做得更好!
我們的應用程序使用基於路由的代碼分割,就足以確保只下載所需要的代碼。但這會導致一些代碼重複。
假設 Home.vue 也需要 lodash。
在這種情況下,從 /about (about .vue) 跳轉到 / (Home.vue) 將會下載 lodash 兩次。
雖然仍然比下載大量冗餘代碼要好,但是如果我們已經知道了有這種依賴關係,那麼做不到被重用就沒有意義,對吧?
這就是 webpack splitChunksPlugin 可以幫助我們的地方。只要在 webpack 的配置中添加下面這幾行代碼,我們就可以將公共依賴項分組到一個單獨的文件中,這樣就可以被共享了。
// webpack.config.js
optimization: {
splitChunks: {
chunks: 'all'
}
}
在 chunk 屬性中,我們只是告訴 webpack 應該優化那些代碼塊。正如您可能已經猜到的那樣,將該屬性設置爲 all 意味着它應該優化所有依賴項。
您可以在 webpack文檔 中閱讀關於此過程的更多信息。
總結
按路由拆分代碼是保持初始加載文件大小較小的最佳(也是最簡單)方法之一。接下來,我們將學習其他部分(Vuex和單獨的組件)也能夠從主文件分割出來並延遲加載。
如果對你有幫助,請關注【前端技能解鎖】: