編寫 universal
程式碼 (也稱為 isomorphic
) 意指編寫可在伺服器和用戶端上運行的程式碼。由於使用案例和平台 API 的差異,我們的程式碼在不同環境中運作時,行為不會完全相同。在此,我們將介紹您需要注意的關鍵事項。
伺服器上的資料響應性
在僅限用戶端的應用程式中,每位使用者都將在其瀏覽器中使用應用程式的全新實例。對於伺服器端渲染,我們也希望如此:每個請求都應具有一個全新的、隔離的應用程式實例,以避免跨請求的狀態污染。
因為實際的渲染過程需要是確定性的,我們也將在伺服器上「預先提取」資料 - 這表示當我們開始渲染時,我們的應用程式狀態將已解析。這表示資料響應性在伺服器上是不必要的,因此預設為停用。停用資料響應性還可以避免將資料轉換為響應式物件的效能成本。
元件生命週期 Hook
由於沒有動態更新,在所有 Vue 生命週期 Hook 中,只有 beforeCreate
和 created
會在 SSR 期間被呼叫。這表示其他生命週期 Hook (例如 beforeMount
或 mounted
) 內的任何程式碼都只會在用戶端上執行。
另一個需要注意的事項是,您應避免在 beforeCreate
和 created
中產生全域副作用的程式碼,例如使用 setInterval
設定計時器。在僅限用戶端的程式碼中,我們可能會設定計時器,然後在 beforeUnmount
或 destroyed
中將其拆除。但是,由於 destroy Hook 不會在 SSR 期間被呼叫,因此計時器將永遠存在。為避免這種情況,請將您的副作用程式碼移至 beforeMount
或 mounted
中。
避免具狀態的單例模式
在編寫僅限用戶端的程式碼時,我們習慣於每次程式碼都會在全新的上下文中評估。然而,Node.js 伺服器是一個長時間運行的進程。當我們的程式碼被要求進入進程時,它將被評估一次,然後保留在記憶體中。這表示如果您建立一個單例物件,它將在每個傳入的請求之間共享。
因此,Quasar CLI 會為每個請求建立一個新的根 Vue 實例,其中包含新的 Router 和 Vuex Store 實例。這類似於每位使用者將在其自己的瀏覽器中使用應用程式的全新實例。如果我們在多個請求之間使用共享實例,則很容易導致跨請求的狀態污染。
您將公開一個工廠函數,而不是直接建立 Router 和 Vuex Store 實例,該函數可以重複執行,以便為每個請求建立全新的應用程式實例
export default function (/* { store, ssrContext } */) {
const Router = new VueRouter({...})
return Router
}
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({...})
return Store
}
如果您正在使用 Vuex 模組 請不要忘記將 state 匯出為函數,否則將會建立單例模式
export default () => ({
...
})
存取平台特定 API
通用程式碼不能假定可以存取平台特定的 API,因此如果您的程式碼直接使用僅限瀏覽器的全域變數 (如 window
或 document
),則在 Node.js 中執行時會拋出錯誤,反之亦然。
對於在伺服器和用戶端之間共享但使用不同平台 API 的任務,建議將平台特定的實作包裝在通用 API 內,或使用為您執行此操作的程式庫。例如,Axios 是一個 HTTP 用戶端,它為伺服器和用戶端公開相同的 API。
對於僅限瀏覽器的 API,常見的方法是在僅限用戶端的生命週期 Hook 內延遲存取它們。
啟動檔案
請注意,如果第三方程式庫的編寫並未考慮通用用途,則可能很難將其整合到伺服器渲染的應用程式中。您*可能*可以通過模擬某些全域變數使其運作,但這會很麻煩,並且可能會干擾其他程式庫的環境偵測程式碼。
當您將第三方程式庫新增至您的專案 (透過啟動檔案) 時,請考慮它是否可以在伺服器和用戶端上運行。如果它只需要在伺服器或僅在用戶端上運行,則在 /quasar.config
檔案中指定此項
return {
// ...
boot: [
'some-boot-file', // runs on both server & client
{ path: 'some-other', server: false } // this boot file gets embedded only on client-side
{ path: 'third', client: false } // this boot file gets embedded only on server-side
]
}
資料預先提取和狀態
在 SSR 期間,我們本質上是在渲染應用程式的「快照」,因此如果應用程式依賴某些非同步資料,則需要在我們開始渲染過程之前預先提取和解析這些資料。
已建立 Quasar CLI PreFetch 功能來解決此問題。花一些時間閱讀相關內容。
此頁面的部分內容取自官方 Vue.js SSR 指南。