編寫 通用
程式碼 (也稱為 同構
程式碼) 意味著編寫同時在伺服器和用戶端運行的程式碼。由於使用案例和平台 API 的差異,我們的程式碼在不同環境中運行時的行為不會完全相同。在這裡,我們將介紹您需要注意的關鍵事項。
伺服器端的資料響應性
在僅限用戶端的應用程式中,每位使用者都將在其瀏覽器中使用應用程式的全新實例。對於伺服器端渲染,我們也希望如此:每個請求都應該有一個全新的、隔離的應用程式實例,這樣就不會發生跨請求的狀態污染。
由於實際的渲染過程需要是確定性的,我們也將在伺服器上「預先擷取」資料 - 這意味著當我們開始渲染時,我們的應用程式狀態將已解析。這表示資料響應性在伺服器上是不必要的,因此預設情況下會停用。停用資料響應性還可以避免將資料轉換為響應式物件的效能成本。
元件生命週期 Hook
由於沒有動態更新,在所有 Vue 生命週期 Hook 中,只有 beforeCreate
和 created
會在 SSR 期間被呼叫。這表示其他生命週期 Hook(例如 beforeMount
或 mounted
)內的任何程式碼都只會在用戶端執行。
另一個需要注意的是,您應該避免在 beforeCreate
和 created
中產生全域副作用的程式碼,例如使用 setInterval
設定計時器。在僅限用戶端的程式碼中,我們可能會設定一個計時器,然後在 beforeUnmount
或 destroyed
中將其拆除。但是,由於 destroy Hook 不會在 SSR 期間被呼叫,因此計時器將永遠存在。為了避免這種情況,請將您的副作用程式碼移至 beforeMount
或 mounted
中。
避免有狀態的單例
當編寫僅限用戶端的程式碼時,我們習慣於每次程式碼都會在全新的上下文中評估。但是,Node.js 伺服器是一個長時間運行的進程。當我們的程式碼被 require 到進程中時,它將被評估一次,然後保留在記憶體中。這表示如果您建立一個單例物件,它將在每個傳入的請求之間共享。
因此,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 內延遲存取它們。
啟動檔案
請注意,如果第三方函式庫在編寫時沒有考慮到通用用法,則可能很難將其整合到伺服器渲染的應用程式中。您可能可以透過模擬某些全域變數使其運作,但這將是 hacky 的,並且可能會干擾其他函式庫的環境偵測程式碼。
當您將第三方函式庫新增到您的專案(透過 啟動檔案)時,請考慮它是否可以在伺服器和用戶端上運行。如果它只需要在伺服器或用戶端上運行,則在 /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 指南。