6.Nuxt.js SSR 实战 - 标准答案

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 }  // 客户端渲染
  }
})

  1. 合理选择渲染模式:SEO 需求用 SSR,静态内容用 SSG
  2. 处理环境差异:用 ClientOnly 包裹客户端组件
  3. 避免水合错误:确保服务端和客户端渲染结果一致
  4. 缓存优化:合理使用页面缓存和 API 缓存
  5. 监控性能:使用 Lighthouse 和 WebPageTest