学习目标
理解 Payload 是什么、为什么 Nuxt 第一次不会请求两次、Payload Cache 到底缓存了什么,以及它和浏览器缓存的区别。
一、这个知识解决什么问题?
上一章我们知道了 useFetch 不会触发两次请求。那么它是怎么做到的?
假设页面这样写:
const { data } = await useFetch('/api/user')
第一次访问时,服务器请求了一次 /api/user。浏览器拿到 HTML 后,Vue 又开始运行——浏览器是不是还会再请求一次?
如果会,那服务器第一次请求不是白做了吗?
这就是 Payload 要解决的问题。
二、一起推导
服务器请求到 { name: "Steven", age: 30 },生成 <div>Steven</div>。
如果浏览器再次发起请求,就是两次网络请求,效率太低。Nuxt 不会这样设计。
继续思考:服务器已经拿到了数据,为什么不一起发给浏览器?这样浏览器就不用再请求了。
于是,Payload 出现了。
三、Payload 是什么?
除了 HTML,服务器还会把本次渲染过程中得到的数据,一起发送给浏览器:
SSR Response
├── HTML(用于显示首屏)
└── Payload(用于恢复 Nuxt 应用状态)
Payload 是 SSR 响应中额外携带的、可序列化的 Nuxt 状态快照。
浏览器 Hydration 时发现数据已经存在,直接读取,不用再次请求接口。
四、脑图
整个过程的核心不是"记住某个 URL",而是 key 与数据的对应关系:
payload.data[key] = serializableResult
服务端调用 useFetch 时,Nuxt 会生成 key 并将数据序列化到 Payload。客户端 Hydration 时,用相同 key 查找数据并恢复 data、status、error 等响应式状态。
能够跨越服务器与浏览器边界的必须是可序列化数据。DOM 节点、函数、数据库连接、原始 Response 对象不应直接放入 Payload。
五、Payload 和浏览器缓存有什么区别?
浏览器缓存:缓存 HTTP Response(图片、CSS、JS),通用、长期、由浏览器管理。
Payload Cache:缓存 SSR 本次渲染的数据,服务于当前 SSR 页面与 Hydration 的状态交接,刷新页面就会重新生成。
两者完全不是一回事。
六、代码实验
创建:
<script setup>
const { data } = await useFetch('/api/time')
</script>
<template>{{ data }}</template>
打开 Chrome DevTools → Network,第一次进入页面,/api/time 通常只有一次真正的请求。浏览器 Hydration 阶段读取的是 Payload,不是再次请求服务器。
七、容易踩坑
❌ Payload 是浏览器缓存
不是。Payload 属于 Nuxt SSR 生命周期,不是 HTTP Cache。
❌ useFetch 只执行一次
不是。Server 和 Client 都会执行,只是 Client 读取的是 Payload,不会重复请求网络。
❌ Payload 永远存在
不是。刷新页面,重新 SSR,Payload 就会重新生成。
一句话记忆
Payload = SSR 数据快照,它让浏览器第一次 Hydration 不必重新请求接口。
系列完结
至此,「Nuxt 深度解析」系列 9 章全部完成。你现在应该建立了一张完整的 Nuxt 心智地图:
Browser 第一次访问
│
▼
Nuxt Server
├── 路由匹配(File-based Routing)
├── 执行 Vue 组件(Auto Import 补全依赖)
│ └── useFetch() 请求数据,存入 Payload
└── 返回 HTML + Payload
│
▼
Browser 显示 HTML(用户立即看到首屏)
│
▼
Hydration(Vue 接管 DOM,用 Payload 恢复状态)
│
▼
onMounted()(此时才能安全访问 window)
│
▼
正常的 Vue 应用运行