Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3
1171 字
6 分钟
Nuxt-SSR.md
2026-03-18
无标签

Nuxt 的服务端渲染(SSR)机制、数据获取策略与同构(isomorphic)执行模型


一、为什么会出现“客户端重复请求”?#

✅ 根本原因:SSR + Hydration(激活)的双重执行模型#

在 Nuxt 的 SSR 模式下,一个页面的渲染分为两个阶段:

阶段执行环境目的
1. 服务端渲染(SSR)Node.js 服务器生成 HTML + 初始状态
2. 客户端激活(Hydration)浏览器将静态 HTML “激活”为交互式 Vue 应用

而像 useFetchuseAsyncData 这样的 数据获取函数,默认会在 两个阶段都执行一次,除非显式控制。

🔍 示例说明#

<script setup>
const { data } = await useFetch('/api/user')
</script>
  • 服务端:Nuxt 在 Node.js 中调用 /api/user,把结果嵌入 HTML。
  • 客户端:Vue 启动时再次调用 /api/user,以“激活”组件状态。

📌 这就是你看到 Network 面板出现两次相同请求 的原因。


二、如何避免客户端重复请求?#

✅ 方案 1:使用 server: false(仅客户端)#

如果你的数据只能在客户端获取(如依赖 localStorage、浏览器 API),则:

const { data } = await useFetch('/api/user', { server: false })

→ 仅在客户端运行,服务端跳过。

✅ 方案 2:使用 lazy: true + 状态复用(推荐用于 SSR)#

但更常见的是:我们希望服务端获取数据,客户端直接复用,不重复请求

Nuxt 实际上 已经自动做了这件事!

关键点:当 useFetchuseAsyncData 在服务端执行后,Nuxt 会:

  1. 将数据序列化到 HTML 中的 <script type="application/json">
  2. 客户端 hydration 时直接读取该数据不会重新发请求

❗ 那为什么你还看到重复请求?#

可能原因:

原因说明
1. 开启了 dev 模式Nuxt Dev Server 有时会为了热更新重发请求(非生产行为)
2. 使用了 $fetch 而不是 useFetch$fetch 不具备状态同步能力,每次都会请求
3. 请求 URL 动态变化(如含时间戳)Nuxt 无法匹配缓存键,视为新请求
4. 未正确使用 useFetch(如放在事件处理器中)放在 onClick 里的 useFetch 永远只在客户端运行

正确写法(自动去重)

<script setup>
// 在 <script setup> 顶层使用 —— Nuxt 会自动处理 SSR + 复用
const { data } = await useFetch('/api/user')
</script>

此时:

  • 服务端:发请求 → 生成 HTML
  • 客户端:从 window.NUXT.data 读取 → 不发新请求

🔗 官方说明:https://nuxt.com/docs/getting-started/data-fetching#avoid-duplicate-requests


三、为什么服务端也能发起请求?#

✅ 因为 Nuxt 内置了 Nitro 服务引擎#

Nuxt 3 使用 Nitro 作为底层服务器框架。它允许你在 server/ 目录中编写 真正的后端代码,运行在 Node.js(或 Serverless)环境中。

因此:

  • useFetch('/api/xxx') 中的 /api/xxx相对路径
  • 在服务端执行时,Nuxt 会 内部代理server/api/xxx.ts不经过网络
  • 在客户端执行时,才真正发起 HTTP 请求到当前域名

💡 这就是所谓的 “同构 API 调用” —— 同一段代码,在服务端走内部函数调用,在客户端走 fetch。

示例流程#

await useFetch('/api/hello')
环境实际行为
服务端(SSR)直接调用 server/api/hello.get.ts 的 handler 函数(零网络开销)
客户端(Hydration)发起 fetch('http://yoursite.com/api/hello')

🔗 文档:https://nuxt.com/docs/guide/concepts/server-engine#internal-api-calls


四、Nuxt 服务端渲染(SSR)完整流程#

以下是 一次页面访问的完整 SSR 流程(以 /user 为例):

sequenceDiagram participant Browser participant NuxtServer as Nuxt (Node.js) participant YourAPI as server/api/*.ts Browser->>NuxtServer: GET /user activate NuxtServer NuxtServer->>YourAPI: 内部调用 useFetch('/api/user') activate YourAPI YourAPI-->>NuxtServer: 返回用户数据 { id: 1, name: 'Alice' } deactivate YourAPI NuxtServer->>NuxtServer: 渲染 Vue 组件(注入数据) NuxtServer->>Browser: 返回 HTML + <script>window.__NUXT__ = { data: ... }</script> deactivate NuxtServer Browser->>Browser: 解析 HTML,显示内容 Browser->>Browser: 加载 client bundle Browser->>Browser: 执行 hydration,复用 __NUXT__.data,不发新请求

关键步骤说明:#

  1. 路由匹配:Nuxt 根据 URL 匹配页面组件(如 pages/user.vue
  2. 服务端数据获取:执行 <script setup> 中的 useFetch / useAsyncData
  3. 内部 API 调用/api/user 被 Nitro 路由到 server/api/user.get.ts
  4. HTML 生成:将数据注入 Vue 组件,生成完整 HTML
  5. 客户端激活:浏览器加载 JS,从 window.__NUXT__ 读取数据,避免重复请求

✅ 总结#

问题答案
为什么有重复请求?Dev 模式、错误使用 $fetch、或未在顶层使用 useFetch
如何避免重复?<script setup> 顶层用 useFetch,Nuxt 自动复用 SSR 数据
服务端为何能发请求?Nitro 引擎支持内部 API 调用,/api/xxx 在服务端是函数调用
SSR 流程?服务端获取数据 → 渲染 HTML → 客户端复用数据 → 激活交互

📚 官方权威参考:

只要遵循 <script setup> 顶层使用 useFetch / useAsyncData,Nuxt 会自动为你处理 SSR 与客户端的数据同步,不会产生不必要的重复请求

Nuxt-SSR.md
https://mizuki.mysqil.com/posts/nuxt-ssr/
作者
ChoriaKiinweill
发布于
2026-03-18
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时