在 Laravel 中搭配 Async Vue Component 使用
Laravel 中,如果是基於要和使用者互動操作的一些介面(元件),透過撰寫 Vue Component 可以很方便地在各個 Laravel Blade 中輕鬆地透過 <Component />
的 tag 方式來重複使用 Vue Component。
目前在 Laravel 的前端打包工具大部分都是透過 laravel-mix 做整合,前陣子也才幫系統的 laravel-mix 升級到 2.0.0 版本,基本上是無痛升級;在去年的「從 laravel-mix 0.8 升級到 1.4 版的記錄與坑」這邊文章提到使用「async/await」的方式現在也不需要安裝額外的 babel-plugin 也都可以使用了,而且現在 Laravel 內你也不需要建立 .babelrc
來設定相關 babel 的設定。
這次主要記錄是怎麼在 Laravel 中設定並使用「非同步載入 的 Vue Component」,Vue.js 官方文件在 Component 部分也有對於 Async Component 也有相關解釋,如果不太了解的話,可以參考一下。
簡單說明一下,在大型的 Application 我們可能會有許多的程式碼,但是我們希望不要一次把所有的程式碼都給打包起來,這樣會造成 bundle 的檔案過大,網頁在載入時會比較慢,而且並不是每個程式碼都會被頁面所需要,這時候我們可以拆分(chunk)程式碼成不同的 bundle 檔案(a.k.a Code Splitting),可以在需要的時候「動態」引入(Dynamic import)進來。
修改 Vue Component 的引入方式
在 Laravel 我們通常會在 /resource/assets/js/app.js
來引入我們的 Vue Component:
這裡的檔案路徑根據官方預設的路徑來做說明。
import FooComponent from './components/FooComponent.vue';Vue.component('foo-component', FooComponent);
Vue.component
的第二個參數接收一個 factory callback function,當你向伺服器發出請求取得 Component 時,會從這裡的 callback function 來執行,factory function 有兩個參數,也就是 Promise 的 resolve
和 reject
,所以當你要載入 Component 時可以透過 resolve
或者你也可以使用 reject
function 來說明載入 Component 為什麼失敗:
Vue.component('foo-component', (resolve, reject) => {/* ... */});
或者你也可以使用 ES6 的 import 語法:
Vue.component('foo-component', () => import('./components/FooComponent.vue'));
這裡為了確保可以執行,我們需要安裝額外的 babel plugin - Syntax Dynamic Import:
$ yarn add babel-plugin-syntax-dynamic-import --dev
接著在 .babelrc
內設定好 plugins
:
{"plugins": ["syntax-dynamic-import"]}
如果你升級到了 laravel-mix 2.0 版本,你可以直接透過 mix.babelConfig()
來直接設定 babel 的設定,更多請參考 laravel-mix 的 Release。
webpack 設定檔案的修改
我們必須要更新 webpack 設定檔的部分,你必須在 output
的地方加上 chunkFilename
:
mix.webpackConfig({output: {chunkFilename: 'js/[name].js',}});
執行 build 可以看到 webpack 的 bundle 後的訊息:
可以看到紅框圈起來的部分,這就是被拆分出來的檔案,大小有 26.4 kB!因為並不是每個地方都會用到它,所以我只在需要的時候在動態引入進來,這樣就可以有效的減少 bundle 後的檔案大小了!
也可以加上 chunkhash(js/[name].[chunkhash].js
):
你也可以加上
[chunkhash]
的選項,這個 hash 就是根據 module 內容所計算出來的 hash 值,也就是說,如果檔案內的內容沒有改變的話,這個 hash 值就不會改變,這樣在瀏覽器就可以有效的被 Cache。
讓我們來測試被 chunk 出來的程式碼會被在特定的頁面會被載入:
你可以透過 Devtools 的 Network(網路) Panel 來查看載入網站的資訊,我們可以找到了加上 chunkhash
的 asset:0.9fe85922805f06a8cafc.js
,點擊「回應(Response)」的部分:
可以看到這是由 webpack 所 chunk 出來的檔案內容。
請注意到黃色圈起來的部分,關於 ETag 這個 header,什麼是 ETag 呢?
ETag Header
根據 Google Web Developer Cache 文件的解釋:
- 伺服器使用 ETag HTTP Header 來傳遞一個驗證的 token
- 你可以透過驗證 token 來有效的對進行資源更新和檢查;如果資源沒有改變,則不會傳輸任何資料
上圖的意思是:如果 client 端向伺服器發送請求時,提供了 If-None-Match
Header 並夾帶 ETag token,伺服器針對目前的資源來檢查 token,如果 token 一致,代表這個資源沒有改變,伺服器將會回傳 304 Not Modified,就可以繼續使用原來的資源。
讓我們回到原來的範例:
當我第一次載入網頁時,網頁回傳的 HTTP Status 是 200,當我再次載入時,請求的 If-None-Match
Header 提供了一個 hash 給伺服器驗證,可以發現這次回傳的 HTTP Status 是 304,代表我的傳送的 ETag hash 和伺服器是一致的,所以可以繼續使用原來 Cache 的檔案。
後記
一開始在升級 laravel-mix 的時候蠻擔心在 build 的時候又會炸掉,這次是從 1.7 版升級到 2.0 版,還好沒有什麼太大的問題,在升級前也先確認 laravel-mix 的 Release 記錄,看看有沒有什麼 Breaking Change 會導致升級炸掉的,後來一切都蠻順利的。
在設定 webpack chunk 中,有遇到一個小問題,在 bundle 的過程中都很順利,檔案也有順利的被拆分出來,但是載入網頁時出現資源找不到的情況(無法載入拆分出來的程式碼),後來追了一下載入的路徑,發現是 webpack 的 publicPath
沒有設定好,導致找不到檔案,如果當你上述步驟都很順利,最後發現載入卻一直失敗,不妨檢查一下!