Nuxt SSR 数据请求的边界
SSR 页面最常见的问题不是“能不能请求到数据”,而是同一段逻辑在服务端和浏览器中拥有不同上下文。
先用白话理解
这篇内容在讲什么?
SSR 是服务器先把页面内容生成好,再交给浏览器添加交互。
问题
Nuxt 水合报错,或者同一接口请求两次,怎样排查?
两端初始数据不同,或通用代码直接使用浏览器对象。
解决方案
- 公开数据使用 useAsyncData
- 浏览器 API 放到 onMounted
- 保持两端模板一致
- 避免重复请求
带状态的 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 和空数组公开内容优先服务端获取
产品信息、文章内容和公开列表适合在服务端输出,提升首屏完整度和搜索可见性,同时避免客户端重复请求。
用户态数据需要隔离
Cookie、请求头和缓存不能跨用户复用。服务端请求必须从当前事件上下文读取身份信息,并谨慎设置共享缓存。
浏览器能力延后执行
localStorage、窗口尺寸和第三方交互库只能在客户端生命周期中使用。通过组件边界或延迟导入,可以避免水合不一致。
- 服务端与客户端使用一致初始结构
- 避免在模板渲染阶段读取窗口对象
- 对客户端专属模块使用动态导入
结语
判断最终要回到真实体验
技术方案没有脱离场景的标准答案。把用户任务、业务边界、开发成本和长期维护放在一起评估,才有可能做出稳定、清晰且经得起变化的页面。
照着做的步骤
- 区分公开数据与登录数据
- 公开内容优先服务端获取
- 浏览器逻辑延后执行
- 保证两端初始结构一致
容易踩的坑
- 服务端直接使用 window
- 不同用户共享缓存
- 服务端和浏览器输出不同内容
术语不用怕
- SSR:服务器端渲染
- 水合:浏览器接管服务器页面
- 客户端:用户浏览器中的运行环境