← 返回内容列表
工程实践

Nuxt SSR 数据请求的边界

SSR 页面最常见的问题不是“能不能请求到数据”,而是同一段逻辑在服务端和浏览器中拥有不同上下文。

先用白话理解

这篇内容在讲什么?

SSR 是服务器先把页面内容生成好,再交给浏览器添加交互。

问题

Nuxt 水合报错,或者同一接口请求两次,怎样排查?

两端初始数据不同,或通用代码直接使用浏览器对象。

解决方案
  1. 公开数据使用 useAsyncData
  2. 浏览器 API 放到 onMounted
  3. 保持两端模板一致
  4. 避免重复请求
带状态的 SSR 请求示例代码
type Product = { id: number; name: string }

const { data, status, error, refresh } = await useAsyncData(
  'product-list',
  () => $fetch<Product[]>('/api/products'),
  { default: () => [] }
)

const products = computed(() => data.value ?? [])

onMounted(() => {
  // 只在浏览器中读取
  const savedView = localStorage.getItem('product-view')
})

// 模板中分别处理 status === 'pending'、error 和空数组
01

公开内容优先服务端获取

产品信息、文章内容和公开列表适合在服务端输出,提升首屏完整度和搜索可见性,同时避免客户端重复请求。

02

用户态数据需要隔离

Cookie、请求头和缓存不能跨用户复用。服务端请求必须从当前事件上下文读取身份信息,并谨慎设置共享缓存。

03

浏览器能力延后执行

localStorage、窗口尺寸和第三方交互库只能在客户端生命周期中使用。通过组件边界或延迟导入,可以避免水合不一致。

  • 服务端与客户端使用一致初始结构
  • 避免在模板渲染阶段读取窗口对象
  • 对客户端专属模块使用动态导入

结语

判断最终要回到真实体验

技术方案没有脱离场景的标准答案。把用户任务、业务边界、开发成本和长期维护放在一起评估,才有可能做出稳定、清晰且经得起变化的页面。

照着做的步骤

  1. 区分公开数据与登录数据
  2. 公开内容优先服务端获取
  3. 浏览器逻辑延后执行
  4. 保证两端初始结构一致

容易踩的坑

  • 服务端直接使用 window
  • 不同用户共享缓存
  • 服务端和浏览器输出不同内容

术语不用怕

  • SSR:服务器端渲染
  • 水合:浏览器接管服务器页面
  • 客户端:用户浏览器中的运行环境