第 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 时效缓存,全面优化项目性能。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
Vckin
讨论数量: 0
发起讨论 只看当前版本


暂无话题~