大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
数据获取
这一课你将能做到
- 在组件中用
fetch+ 异步加载列表,并处理 loading / error / 空数据三态 - 在
onMounted或 watch 路由参数时发起请求,并处理竞态 - 对照本站:UI 用前端框架,业务数据仍多来自 Spring Boot
/api
为什么要学
真实页面几乎都要远程数据:课程列表、用户信息、提交结果。
不会管三态,就会白屏、闪错、或显示过期列表。Vue 不绑定特定请求库——fetch 或 axios 皆可;本课用 fetch 打底。
先建立图像
进入页面 / slug 变化
→ loading=true
→ fetch /api/...
→ 成功:写入 ref;失败:error 文案
→ loading=false
模板根据三态分支渲染
| 状态 | UI |
|---|---|
| loading | 骨架/文案「加载中」 |
| error | 可重试提示 |
| success 空 | 「暂无数据」 |
| success 有 | 列表 |
跟着做
步骤 1:最小三态
<script setup>
import { ref, onMounted } from "vue";
const tracks = ref([]);
const loading = ref(false);
const error = ref("");
async function load() {
loading.value = true;
error.value = "";
try {
const res = await fetch("/api/tracks");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
tracks.value = await res.json();
} catch (e) {
error.value = e.message || "加载失败";
} finally {
loading.value = false;
}
}
onMounted(load);
</script>
<template>
<p v-if="loading">加载中…</p>
<p v-else-if="error">
{{ error }}
<button type="button" @click="load">重试</button>
</p>
<p v-else-if="tracks.length === 0">暂无课程</p>
<ul v-else>
<li v-for="t in tracks" :key="t.id || t.slug">{{ t.title }}</li>
</ul>
</template>
本地若无 /api/tracks,会走 error 分支——正好练习重试 UI。可用 JSON Placeholder 或 Vite 代理到本站后端。
步骤 2:Vite 代理(可选)
// vite.config.js
export default {
server: {
proxy: {
"/api": "http://localhost:8080",
},
},
};
指向本机 Spring Boot,与 React/Next 课 同一数据源思想。
步骤 3:随路由参数重新加载
<script setup>
import { watch } from "vue";
import { useRoute } from "vue-router";
const route = useRoute();
watch(
() => route.params.slug,
(slug) => {
if (slug) loadTrack(slug);
},
{ immediate: true }
);
</script>
组件被复用时,仅 onMounted 不够——必须 watch 参数。
步骤 4:竞态守卫
let seq = 0;
async function loadTrack(slug) {
const my = ++seq;
loading.value = true;
try {
const res = await fetch(`/api/tracks/${slug}`);
const data = await res.json();
if (my !== seq) return; // 过期响应丢弃
track.value = data;
} finally {
if (my === seq) loading.value = false;
}
}
快速切换路由时,旧请求后返回不该覆盖新页。
步骤 5:抽 useFetchList composable(可选)
把 loading/error/data/load 推进 composable,多页复用——衔接上一课。
步骤 6:对照 React
React 课用 useEffect 取数;Vue 用 onMounted/watch。
两边都要:依赖变化重取、清理/竞态、三态 UI。本站真相在 Boot,不在前端内存。
讲清楚要点
- fetch 不把 404 当 throw:要自己查
res.ok。 - JSON 解析失败也进 catch。
- 凭证与 cookie:跨域要 CORS;同源代理更省事。
- 别把 token 写死进仓库;用环境变量。
- 列表与详情接口分离:对齐本站 track list vs lesson content。
- 错误信息对人话:
HTTP 500可再映射成「服务暂不可用」。
你可能会懵的地方
-
为什么有时开发成功、生产失败?
代理只在 dev;生产需同源或正确 API 域名。 -
axios 要不要?
团队统一即可;会 fetch 就够迁移。 -
Suspense?
Vue 3 支持异步组件悬停;入门先显式三态更直观。 -
POST 提交?
同模式:method:'POST',headers,body;仍要错误处理。 -
和 Pinia?
缓存可进 store,但要定失效;先会组件内取数。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 只写成功路径 | 白屏难查 | 三态齐全 |
| 忽略 res.ok | 把错误 HTML 当 JSON | 检查状态码 |
| 只 onMounted 不 watch 参数 | 路由切换数据旧 | watch slug |
| 无竞态处理 | 显示错页数据 | 序号/AbortController |
| 前端写死业务规则当唯一校验 | 不安全 | 后端再验 |
| 把 Boot 重写成一堆 mock 就算完成 | 假完成 | 至少对接一次真 API 或明确 mock 边界 |
自测三问
- 三态分别对应什么 UI?
- 为何
res.ok必须检查? - 路由参数变化时如何避免竞态?
小结一句话
用 fetch 拉远端数据时,把 loading/error/空/成功写全,并在路由变化与快速切换下保持结果正确。
官方延伸
- MDN fetch
- 下一课:构建与部署