第 4 章:数据获取
第 4 章:数据获取(SSR/CSR 数据方案)
本章围绕 Nuxt 4 全栈数据交互展开,先讲解三大渲染模式的执行逻辑与业务选型,再依次实现服务端接口、客户端请求、服务端请求、跨域代理、数据缓存等能力,全部搭配可运行代码与实战演练,适配小册平台的数据业务场景。
4.1 数据渲染模式:SSR、SSG、ISR 实战对比
Nuxt 4 支持三种主流渲染模式,直接决定数据执行时机、SEO 效果、访问性能,是内容平台开发的核心选型依据。
1. SSR 服务端渲染(默认模式)
- 执行逻辑:用户每访问一次页面,服务端实时请求接口、拼接完整 HTML 后返回浏览器,数据在服务端执行。
- 数据特点:数据实时更新,搜索引擎可抓取完整页面内容,SEO 最优。
- 适用场景:小册首页、详情页、评论区、实时榜单等动态内容页面。
- 启动 / 打包命令:yarn dev / yarn build
2. SSG 静态站点生成
- 执行逻辑:执行打包命令时,一次性拉取所有页面数据,预生成纯静态 HTML 文件,后续访问不再请求服务端。
- 数据特点:访问速度最快、服务器压力极小;缺点是数据无法自动更新,修改内容后必须重新打包。
- 适用场景:帮助文档、平台介绍页、固定不变的栏目页。
- 打包命令:yarn generate
3. ISR 增量静态再生
- 执行逻辑:结合 SSG + SSR 优势,打包生成静态页面并设置缓存时效;缓存过期后,后台静默重新拉取数据更新页面,无需全量重新打包。
- 数据特点:兼顾静态访问速度与数据动态更新。
- 适用场景:小册列表、每日推荐、资讯专栏等更新频率适中的页面。
- ISR 代码配置
在页面中通过 definePageMeta 设置缓存时长(单位:秒)
<script setup>
// 页面缓存 300 秒,超时自动增量更新
definePageMeta({
isr: 300
})
</script>
实战演练
- 1,分别执行 yarn build(SSR)、yarn generate(SSG)打包项目,对比页面数据加载效果。
- 2,给小册列表页开启 isr 配置,观察缓存过期后数据自动更新逻辑。
- 3,结合小册业务,区分哪些页面使用 SSR、SSG、ISR。
4.2 接口封装:server/api 目录与 Nitro 接口
Nuxt 4 内置 Nitro 服务引擎,server/api 目录遵循文件即接口规则,无需额外搭建后端项目,一套代码完成前后端开发。
目录规则
- 文件路径:server/api/xxx/xxx.ts
- 自动映射接口地址:/api/xxx/xxx
1. 基础 GET 接口(小册列表)
新建文件 server/api/book/list.ts
export default defineEventHandler(() => {
// 模拟数据库数据
const bookList = [
{ id: 1, title: "Nuxt4 全栈实战", author: "技术作者", views: 5200 },
{ id: 2, title: "Vue3 进阶指南", author: "前端达人", views: 3600 }
]
return {
code: 200,
data: bookList,
msg: "请求成功"
}
})
2. 接收 GET 路由参数(小册详情)
新建文件 server/api/book/detail.ts,使用 getQuery 获取地址参数
export default defineEventHandler((event) => {
// 获取 URL 上的 query 参数
const query = getQuery(event)
const bookId = query.id
return {
code: 200,
data: {
id: bookId,
title: "小册详情内容",
author: "技术作者",
views: 200,
content: "这里是教程正文..."
}
}
})
3. 接收 POST 请求体参数
server/api/book/add.ts
export default defineEventHandler(async (event) => {
// 读取 POST 请求 body
const body = await readBody(event)
return {
code: 200,
data: body,
msg: "新增小册成功"
}
})
实战演练
- 1,在 server/api 目录下创建 book 目录,新建文件 server/api/book/list.ts、server/api/book/detail.ts、server/api/book/add.ts。
- 2,依次创建小册列表、详情、新增三个接口。
- 3,本地运行项目,通过地址栏 / 接口测试工具访问接口,验证数据正常返回。
- 4,测试 GET 传参、POST 传参两种参数接收方式。
4.3 客户端数据获取:$fetch 与请求拦截
客户端请求在浏览器端执行,适合后台管理、用户中心、点击触发类交互,使用 Nuxt 内置全局 $fetch,并可统一配置请求 / 响应拦截。
1. $fetch 基础用法
用户中心
app/pages/users/index.vue
<template>
<div>
<h3>个人中心</h3>
<button @click="getUserInfo">获取个人信息</button>
<div v-if="user">
<p>用户名:{{ user.name }}</p>
<p>账号ID:{{ user.uid }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const user = ref(null)
// 客户端点击后发起请求
const getUserInfo = async () => {
const res = await $fetch('/api/user/info')
user.value = res.data
}
</script>
用户查询接口
server/api/user/info.ts
export default defineEventHandler(() => {
return {
code: 200,
data: {
name: "技术作者",
uid: "123456",
}
}
})
访问 localhost:3000/users,点击获取个人信息按钮,查看浏览器网络面板。
2. 全局请求拦截(统一处理 Token、错误提示)
在 app/plugins/ 新建 fetch-intercept.ts 全局插件,实现全局拦截:
app/plugins/fetch-intercept.ts
export default defineNuxtPlugin((nuxtApp) => {
const customFetch = $fetch.create({
onRequest({ options }) {
const token = useCookie('token').value
if (token) {
options.headers.set('Authorization', `Bearer ${token}`)
}
console.log("请求加载成功")
},
onResponse({ response }) {
const res = response._data
if (res.code !== 200) {
alert(`请求失败:${res.msg}`)
}
},
onResponseError() {
alert("网络异常,请稍后重试")
}
})
console.log("fetch 插件加载成功")
// 挂载到全局, $fetch
nuxtApp.provide('fetch', customFetch)
})
3. 使用自定义拦截请求
const { $fetch } = useNuxtApp()
const res = await $fetch('/api/user/info')
app/pages/users/index.vue
<template>
<div>
<h3>个人中心</h3>
<button @click="getUserInfo">获取个人信息</button>
<div v-if="user">
<p>用户名:{{ user.name }}</p>
<p>账号ID:{{ user.uid }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const { $fetch } = useNuxtApp()
const user = ref(null)
// 客户端点击后发起请求
const getUserInfo = async () => {
const res = await $fetch('/api/user/info')
user.value = res.data
}
</script>
访问 localhost:3000/users,点击获取个人信息按钮,查看浏览器网络面板。
实战演练
- 1,在用户中心页面编写 $fetch 点击请求逻辑,查看浏览器网络面板。
- 2,配置全局请求拦截,模拟无 Token、接口报错场景,验证拦截生效。
- 3,区分客户端请求与服务端请求的网络表现差异。
4.4 服务端数据获取:useAsyncData 与 useFetch
服务端请求在 SSR/SSG 阶段执行,数据随页面 HTML 直出,SEO 友好,是内容类页面首选方案。Nuxt 提供两个核心 API:useFetch(简易封装)、useAsyncData(底层灵活)。
1. useFetch(推荐,日常高频使用)
内置加载状态、错误捕获、自动解析数据,语法简洁。
app/pages/books/index.vue 小册列表页
<template>
<div>
<h2>小册列表</h2>
<!-- 加载中 -->
<div v-if="status === 'pending'">数据加载中...</div>
<!-- 正常渲染 -->
<ul v-else>
<li v-for="item in data.data" :key="item.id">
{{ item.title }} - {{ item.author }}
</li>
</ul>
</div>
</template>
<script setup>
// 服务端发起请求,无需手动 async/await 外部包裹
const { data, status} = await useFetch('/api/book/list')
</script>
自定义请求方式与参数
const { data } = await useFetch('/api/book/add', {
method: 'POST',
body: { title: "新小册", author: "测试作者" }
})
2. useAsyncData(底层 API,复杂场景使用)
支持自定义缓存 Key、数据处理逻辑,适合多数据源、复杂数据加工场景。
<script setup>
const { data, pending, error } = await useAsyncData(
'bookListKey', // 全局唯一缓存标识
async () => {
// 自定义请求逻辑
const res = await $fetch('/api/book/list')
return res.data
}
)
</script>
实战演练
- 1,在小册列表页使用 useFetch 获取接口数据并渲染列表。
- 2,故意写错接口地址,测试 error 错误展示效果。
- 3,改用 useAsyncData 重构代码,理解缓存 Key 的作用。
4.5 跨域处理与代理配置
当项目对接第三方接口、外部独立后端时,通过 Nuxt 代理和 CORS 配置解决跨域问题。
1. 开发环境接口代理(nuxt.config.ts)
将前端请求路径转发到目标外网接口,解决浏览器跨域。
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
devProxy: {
// 匹配 /api-external 开头的请求
'/api-external': {
target: 'https://api.example.com', // 目标接口地址
changeOrigin: true, // 开启跨域
prependPath: true
}
}
}
})
前端调用代理接口
const { data } = await useFetch('/api-external/news')
2. 全局 CORS 跨域(允许外部访问本项目接口)
在 server/middleware/ 新建 cors.ts,全局设置跨域响应头:
// server/middleware/cors.ts
export default defineEventHandler((event) => {
setResponseHeaders(event, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type,Authorization'
})
// 拦截 OPTIONS 预检请求
if (event.method === 'OPTIONS') {
return setResponseStatus(event, 204)
}
})
实战演练
- 1,配置 devProxy 代理公共测试接口,前端发起请求验证跨域解决。
- 2,配置 CORS 中间件,使用其他域名 / 工具访问本项目接口。
4.6 数据缓存与更新策略
合理使用缓存可以减少重复请求、提升页面速度,结合小册业务区分不同缓存方案。
1. 关闭 / 开启请求缓存
useFetch / useAsyncData 默认根据 key 缓存数据,同一页面多次调用只请求一次。
// 关闭缓存,每次都重新请求
const { data } = await useFetch('/api/book/list', {
cache: false
})
2. 手动刷新数据
使用 refresh() 方法主动重新请求接口,适用于发布内容、点赞、刷新列表等场景。
<template>
<div>
<button @click="refreshList">刷新小册列表</button>
<ul>
<li v-for="item in data" :key="item.id">{{ item.title }}</li>
</ul>
</div>
</template>
<script setup>
const { data, refresh } = await useFetch('/api/book/list')
const refreshList = () => {
refresh() // 手动重新拉取数据
}
</script>
3. 业务分层缓存策略(小册)
- 首页推荐小册:使用 ISR 定时缓存,5~10 分钟自动更新
- 小册详情页:SSR + 短时缓存,保证内容实时性
- 个人中心 / 后台:关闭全局缓存,操作后手动 refresh 刷新
- 静态介绍页:使用 SSG 全静态缓存
实战演练
- 1,在同一页面多次调用 useFetch,验证默认缓存生效。
- 2,绑定刷新按钮,测试 refresh 方法动态更新列表。
- 3,为不同页面配置对应缓存规则。
本章综合实战任务(小册)
- 在小册项目中,完成 server/api 完成小册列表、详情、用户信息整套接口开发。
- 首页、小册列表页使用 useFetch 服务端请求,保障 SEO。
- 个人中心使用 $fetch 客户端请求,配置全局请求拦截。
- 配置接口代理,对接外部测试接口,解决跨域问题。
- 按业务场景为页面配置 SSR/ISR/SSG 及缓存策略。
本章总结
- 渲染模式:SSR 实时动态、SSG 极速静态、ISR 增量更新,按需选型。
- Nitro 接口:server/api 快速编写服务端接口,实现全栈一体化开发。
- 客户端请求:$fetch 搭配全局拦截,适用于交互类页面。
- 服务端请求:useFetch / useAsyncData 为内容页面首选,SEO 友好。
- 跨域方案:devProxy 开发代理、cors 中间件实现接口跨域。
- 缓存策略:内置缓存、手动刷新、ISR 时效缓存,全面优化项目性能。
nuxt4实战教程
关于 LearnKu