[Lv2] Nuxt 3 Lifecycle 與 Hydration 原理
深入理解 Nuxt 3 的生命週期(Lifecycle)、狀態管理(State Management)與 Hydration 機制,避免常見的 Hydration Mismatch 問題。
1. 面試回答主軸
- Lifecycle 差異:區分 Server-side 與 Client-side 執行的 Hooks。
setup兩端都會執行,onMounted僅在 Client 端執行。 - 狀態管理:理解
useState與ref在 SSR 場景下的差異。useState能在 Server 與 Client 間同步狀態,避免 Hydration Mismatch。 - Hydration 機制:解釋 Hydration 是如何讓靜態 HTML 變為可互動應用,以及常見的 Mismatch 原因(HTML 結構不一致、隨機內容等)。
2. Server-side vs Client-side Lifecycle
2.1 Lifecycle Hooks 執行環境
在 Nuxt 3 (Vue 3 SSR) 中,不同的 Hooks 會在不同的環境下執行:
| Lifecycle Hook | Server-side | Client-side | 說明 |
|---|---|---|---|
| setup() | ✅ 執行 | ✅ 執行 | 組件初始化邏輯。注意:避免在 setup 中使用僅 Client 端的 API(如 window, document)。 |
| onBeforeMount | ❌ 不執行 | ✅ 執行 | 掛載前。 |
| onMounted | ❌ 不執行 | ✅ 執行 | 掛載完成。DOM 操作、Browser API 呼叫應放在這裡。 |
| onBeforeUpdate | ❌ 不執行 | ✅ 執行 | 資料更新前。 |
| onUpdated | ❌ 不執行 | ✅ 執行 | 資料更新後。 |
| onBeforeUnmount | ❌ 不執行 | ✅ 執行 | 卸載前。 |
| onUnmounted | ❌ 不執行 | ✅ 執行 | 卸載後。 |
2.2 常見面試題:onMounted 在 Server 端會執行嗎?
回答:
不會。onMounted 只會在 Client 端(瀏覽器)執行。Server 端渲染只負責生成 HTML 字串,不會進行 DOM 的掛載(Mounting)。
延伸問題:如果在 Server 端需要執行特定邏輯怎麼辦?
- 使用
setup()或useAsyncData/useFetch。 - 如果需要區分環境,可以使用
process.server或process.client判斷。
<script setup>
// Server 和 Client 都會執行
console.log('Setup executed');
if (process.server) {
console.log('Only on Server');
}
onMounted(() => {
// 只有 Client 會執行
console.log('Mounted (Client Only)');
// 安全地使用 window
window.alert('Hello');
});
</script>
3. Nuxt 3 useState vs Vue ref
3.1 為什麼 Nuxt 需要 useState?
在 SSR 應用中,Server 端渲染完 HTML 後,會將狀態(State)序列化並傳送給 Client 端,以便 Client 端進行 Hydration(接手狀態)。
- Vue
ref:是組件內的局部狀態。在 SSR 過程中,Server 端建立的ref值不會自動傳輸給 Client 端。Client 端初始化時會重新建立ref(通常重置為初始值),導致 Server 渲染的內容與 Client 初始狀態不一致,產生 Hydration Mismatch。 - Nuxt
useState:是 SSR 友善的狀態管理。它會將狀態儲存在NuxtPayload中,隨著 HTML 一起傳送給 Client。Client 端初始化時會讀取這個 Payload,還原狀態,確保 Server 與 Client 狀態一致。
3.2 比較表
| 特性 | Vue ref / reactive | Nuxt useState |
|---|---|---|
| 作用域 | 組件內 / 模組內 | 全域(App 範圍內可透過 key 共享) |
| SSR 狀態同步 | ❌ 不會同步 | ✅ 自動序列化並同步到 Client |
| 適用場景 | 僅 Client 端互動狀態、不需要 SSR 同步的資料 | 跨組件狀態、需要從 Server 帶到 Client 的資料(如 User Info) |
3.3 實作範例
錯誤示範(使用 ref 做跨端狀態):
// Server 端產生隨機數 -> HTML 顯示 5
const count = ref(Math.random());
// Client 端重新執行 -> 產生新的隨機數 3
// 結果:Hydration Mismatch (Server: 5, Client: 3)
正確示範(使用 useState):
// Server 端產生隨機數 -> 存入 Payload (key: 'random-count')
const count = useState('random-count', () => Math.random());
// Client 端讀取 Payload -> 取得 Server 產生的值
// 結果:狀態一致
4. Hydration 與 Hydration Mismatch
4.1 什麼是 Hydration?
Hydration(注水)是指 Client 端 JavaScript 接管由 Server 端渲染的靜態 HTML 的過程。
- Server Rendering:Server 執行 Vue 應用,生成 HTML 字串(包含內容與 CSS)。
- HTML 下載:瀏覽器下載並顯示靜態 HTML(First Paint)。
- JS 下載與執行:瀏覽器下載 Vue/Nuxt 的 JS bundle。
- Hydration:Vue 在 Client 端重新建立虛擬 DOM (Virtual DOM),比對現有的真實 DOM。如果結構一致,Vue 就會「啟用」這些 DOM 元素(綁定事件監聽器),讓頁面變為可互動。