Nuxt 和水合
為什麼解決水合問題很重要
在開發過程中,您可能會遇到水合問題。不要忽視這些警告。
為什麼解決它們很重要?
水合不匹配不僅僅是警告——它們是嚴重問題的指示器,可能導致您的應用程式崩潰
效能影響
- 增加互動時間:水合錯誤會強制 Vue 重新渲染整個元件樹,這將增加您的 Nuxt 應用變得可互動所需的時間
- 糟糕的使用者體驗:使用者可能會看到內容閃爍或意外的佈局偏移
功能問題
- 互動中斷:事件監聽器可能無法正確附加,導致按鈕和表單無法正常工作
- 狀態不一致:應用程式狀態可能與使用者所見和應用程式認為已渲染的內容不同步
- SEO 問題:搜尋引擎可能索引與使用者實際看到的內容不同的內容
如何檢測它們
開發控制檯警告
在開發過程中,Vue 會在瀏覽器控制檯中記錄水合不匹配警告
常見原因
伺服器上下文中的僅瀏覽器 API
問題:在伺服器端渲染期間使用瀏覽器特有的 API。
<template>
<div>User preference: {{ userTheme }}</div>
</template>
<script setup>
// This will cause hydration mismatch!
// localStorage doesn't exist on the server!
const userTheme = localStorage.getItem('theme') || 'light'
</script>
解決方案:您可以使用 useCookie
<template>
<div>User preference: {{ userTheme }}</div>
</template>
<script setup>
// This works on both server and client
const userTheme = useCookie('theme', { default: () => 'light' })
</script>
不一致的資料
問題:伺服器和客戶端之間的資料不同。
<template>
<div>{{ Math.random() }}</div>
</template>
解決方案:使用 SSR 友好的狀態
<template>
<div>{{ state }}</div>
</template>
<script setup>
const state = useState('random', () => Math.random())
</script>
基於客戶端狀態的條件渲染
問題:在 SSR 期間使用僅客戶端條件。
<template>
<div v-if="window?.innerWidth > 768">
Desktop content
</div>
</template>
解決方案:使用媒體查詢或在客戶端處理
<template>
<div class="responsive-content">
<div class="hidden md:block">Desktop content</div>
<div class="md:hidden">Mobile content</div>
</div>
</template>
具有副作用的第三方庫
問題:修改 DOM 或具有瀏覽器依賴項的庫(這在標籤管理器中經常發生)。
<script setup>
if (import.meta.client) {
const { default: SomeBrowserLibrary } = await import('browser-only-lib')
SomeBrowserLibrary.init()
}
</script>
解決方案:在水合完成後初始化庫
<script setup>
onMounted(async () => {
const { default: SomeBrowserLibrary } = await import('browser-only-lib')
SomeBrowserLibrary.init()
})
</script>
基於時間的動態內容
問題:內容根據當前時間變化。
<template>
<div>{{ greeting }}</div>
</template>
<script setup>
const hour = new Date().getHours()
const greeting = hour < 12 ? 'Good morning' : 'Good afternoon'
</script>
解決方案:使用 NuxtTime
元件或在客戶端處理
<template>
<div>
<NuxtTime :date="new Date()" format="HH:mm" />
</div>
</template>
<template>
<div>
<ClientOnly>
{{ greeting }}
<template #fallback>
Hello!
</template>
</ClientOnly>
</div>
</template>
<script setup>
const greeting = ref('Hello!')
onMounted(() => {
const hour = new Date().getHours()
greeting.value = hour < 12 ? 'Good morning' : 'Good afternoon'
})
</script>
總結
- 使用 SSR 友好的組合式函式:
useFetch
、useAsyncData
、useState
- 包裝僅客戶端程式碼:對於瀏覽器特定的內容,使用
ClientOnly
元件 - 一致的資料來源:確保伺服器和客戶端使用相同的資料
- 避免在 setup 中產生副作用:將依賴於瀏覽器的程式碼移至
onMounted
您可以閱讀Vue 關於 SSR 水合不匹配的文件以更好地理解水合。