Nuxt.js服务端渲染SSR #

一、SSR概述 #

服务端渲染(Server-Side Rendering)是在服务器端生成完整 HTML 页面的技术。Nuxt.js 默认使用 SSR 模式。

1.1 SSR优势 #

  • SEO友好:搜索引擎可以抓取完整内容
  • 首屏快速:用户更快看到页面内容
  • 社交分享:Open Graph 标签正确渲染
  • 渐进增强:支持禁用 JavaScript 的浏览器

1.2 SSR流程 #

text
用户请求
    ↓
服务器接收请求
    ↓
执行 Vue 组件
    ↓
生成 HTML
    ↓
发送到客户端
    ↓
客户端水合(Hydration)
    ↓
应用变为可交互

二、渲染模式 #

2.1 服务端渲染(SSR) #

默认模式,每次请求都在服务器生成 HTML。

typescript
export default defineNuxtConfig({
  ssr: true
})

2.2 客户端渲染(CSR) #

仅在客户端渲染,适合不需要 SEO 的应用。

typescript
export default defineNuxtConfig({
  ssr: false
})

2.3 混合渲染 #

页面级别选择渲染模式:

typescript
export default defineNuxtConfig({
  routeRules: {
    '/': { ssr: true },
    '/admin/**': { ssr: false },
    '/blog/**': { ssr: true }
  }
})

2.4 页面级配置 #

vue
<script setup lang="ts">
definePageMeta({
  ssr: false
})
</script>

三、数据获取 #

3.1 服务端数据获取 #

vue
<script setup lang="ts">
const { data } = await useFetch('/api/articles')

const { data: user } = await useAsyncData(
  'user',
  () => $fetch('/api/user')
)
</script>

3.2 仅客户端数据 #

vue
<script setup lang="ts">
const { data } = await useFetch('/api/user-preferences', {
  server: false
})
</script>

3.3 仅服务端数据 #

vue
<script setup lang="ts">
const { data } = await useFetch('/api/internal', {
  client: false
})
</script>

四、生命周期 #

4.1 服务端生命周期 #

vue
<script setup lang="ts">
if (import.meta.server) {
  console.log('Running on server')
}

onServerPrefetch(async () => {
  await fetchData()
})
</script>

4.2 客户端生命周期 #

vue
<script setup lang="ts">
onMounted(() => {
  console.log('Component mounted on client')
})

onBeforeMount(() => {
  console.log('Before mount on client')
})
</script>

4.3 通用生命周期 #

vue
<script setup lang="ts">
onCreated(() => {
  console.log('Created on both server and client')
})
</script>

五、状态管理 #

5.1 状态共享 #

vue
<script setup lang="ts">
const state = useState('shared-state', () => ({
  count: 0,
  user: null
}))
</script>

5.2 状态水合 #

Nuxt 自动处理状态从服务端到客户端的传输。

vue
<script setup lang="ts">
const { data } = await useAsyncData('key', () => fetchData())

if (import.meta.server) {
  state.value = serverState
}
</script>

5.3 避免水合不匹配 #

vue
<script setup lang="ts">
const mounted = ref(false)

onMounted(() => {
  mounted.value = true
})
</script>

<template>
  <div v-if="mounted">
    Client-only content
  </div>
</template>

六、ClientOnly组件 #

6.1 基本用法 #

vue
<template>
  <ClientOnly>
    <ClientSideComponent />
  </ClientOnly>
</template>

6.2 降级内容 #

vue
<template>
  <ClientOnly>
    <HeavyChart />
    <template #fallback>
      <div>Loading chart...</div>
    </template>
  </ClientOnly>
</template>

6.3 条件渲染 #

vue
<script setup lang="ts">
const isClient = ref(false)

onMounted(() => {
  isClient.value = true
})
</script>

<template>
  <div v-if="isClient">
    <ClientOnlyComponent />
  </div>
</template>

七、环境检测 #

7.1 import.meta #

typescript
if (import.meta.server) {
  console.log('Server environment')
}

if (import.meta.client) {
  console.log('Client environment')
}

if (import.meta.dev) {
  console.log('Development mode')
}

if (import.meta.prod) {
  console.log('Production mode')
}

7.2 process #

typescript
if (process.server) {
  console.log('Server')
}

if (process.client) {
  console.log('Client')
}

7.3 useHydration #

vue
<script setup lang="ts">
const { data, error } = await useHydration(
  'unique-key',
  () => fetchOnServer(),
  () => fetchOnClient()
)
</script>

八、常见问题 #

8.1 水合不匹配 #

原因:服务端和客户端渲染结果不一致

解决:

vue
<script setup lang="ts">
const date = ref('')

onMounted(() => {
  date.value = new Date().toLocaleString()
})
</script>

8.2 window未定义 #

原因:服务端没有 window 对象

解决:

vue
<script setup lang="ts">
if (import.meta.client) {
  console.log(window.location.href)
}
</script>

8.3 第三方库问题 #

vue
<script setup lang="ts">
let MyLibrary

onMounted(async () => {
  MyLibrary = (await import('my-library')).default
})
</script>

九、性能优化 #

9.1 缓存策略 #

typescript
export default defineNuxtConfig({
  routeRules: {
    '/api/**': { cache: { maxAge: 60 } },
    '/blog/**': { isr: 3600 }
  }
})

9.2 流式渲染 #

vue
<script setup lang="ts">
const { data } = await useFetch('/api/data', {
  lazy: true
})
</script>

9.3 预渲染 #

typescript
export default defineNuxtConfig({
  nitro: {
    prerender: {
      routes: ['/', '/about', '/contact']
    }
  }
})

十、调试SSR #

10.1 服务端日志 #

vue
<script setup lang="ts">
if (import.meta.server) {
  console.log('Server render:', useRoute().path)
}
</script>

10.2 客户端日志 #

vue
<script setup lang="ts">
onMounted(() => {
  console.log('Client hydrated')
})
</script>

10.3 DevTools #

使用 Nuxt DevTools 查看 SSR 状态。

十一、最佳实践 #

11.1 数据获取 #

  • 使用 useFetchuseAsyncData
  • 避免在 onMounted 中获取 SSR 数据
  • 使用 server: false 排除不需要 SSR 的数据

11.2 组件设计 #

  • 保持组件纯净
  • 避免直接操作 DOM
  • 使用 ClientOnly 包装客户端组件

11.3 状态管理 #

  • 使用 useState 共享状态
  • 避免在服务端使用客户端存储
  • 正确处理状态水合

十二、总结 #

本章介绍了 Nuxt.js 服务端渲染:

  • SSR 原理和优势
  • 渲染模式选择
  • 数据获取策略
  • 生命周期处理
  • 状态管理和水合
  • 常见问题解决
  • 性能优化技巧

SSR 是 Nuxt.js 的核心特性,下一章我们将学习静态站点生成。

最后更新:2026-03-28