Nuxt.js SSR 实践:如何处理客户端/服务端差异?如何优化 SSR 性能?
| 模式 | 渲染时机 | 适用场景 | 性能 |
|---|---|---|---|
| SSR | 每次请求时渲染 | 动态内容、SEO | 中 |
| SSG | 构建时预渲染 | 静态内容、博客 | 高 |
| CSR | 浏览器渲染 | 后台管理系统 | 低 |
// Nuxt3 - useFetch (服务端+客户端都会执行)
const { data: posts } = await useFetch('/api/posts')
// useAsyncData (自定义异步逻辑)
const { data } = await useAsyncData('posts', () => $fetch('/api/posts'))
// 仅客户端执行
onMounted(() => {
// 访问 window, document 等
})
<template>
<!-- 仅客户端渲染 -->
<ClientOnly>
<Chart /> <!-- 依赖 window 的组件 -->
</ClientOnly>
</template>
<script setup>
// 判断环境
if (import.meta.server) {
// 服务端逻辑
}
if (import.meta.client) {
// 客户端逻辑
}
</script>
问题1:水合不匹配(Hydration Mismatch)
// ❌ 错误:服务端和客户端渲染结果不同
<div>{{ new Date().getTime() }}</div> // 时间戳每次都不同
// ✅ 正确:使用 ClientOnly 或统一数据源
<ClientOnly>
<div>{{ timestamp }}</div>
</ClientOnly>
问题2:window/document 未定义
// ❌ 错误:服务端没有 window
const width = window.innerWidth
// ✅ 正确:判断环境或使用 ClientOnly
const width = import.meta.client ? window.innerWidth : 0
// 1. 缓存策略
const { data } = await useFetch('/api/posts', {
getCachedData(key) {
return nuxtApp.payload.data[key] || nuxtApp.static.data[key]
}
})
// 2. 懒加载组件
const HeavyComponent = defineAsyncComponent(() =>
import('~/components/HeavyComponent.vue')
)
// 3. 路由缓存
export default defineNuxtConfig({
routeRules: {
'/blog/**': { swr: 3600 }, // 1小时缓存
'/admin/**': { ssr: false } // 客户端渲染
}
})
- 合理选择渲染模式:SEO 需求用 SSR,静态内容用 SSG
- 处理环境差异:用
ClientOnly包裹客户端组件 - 避免水合错误:确保服务端和客户端渲染结果一致
- 缓存优化:合理使用页面缓存和 API 缓存
- 监控性能:使用 Lighthouse 和 WebPageTest