本节摘要:前端把网络请求当成"三态状态机"来管:加载中、成功、失败。主角是请求编排(时机、清理、竞态),而不是某个库的魔法。本节用 fetch 与组件配合,讲清三态、请求时机与"请求竞态"这个最容易踩的坑,并给出可复用的处理模式。
本节阅读目标
阅读完本节,你应当能够:
请求不是"发起-拿到"两步,而是三种状态:发起(加载中)、成功(有数据)、失败(有错误)。界面若不区分它们,会呈现"点击没反应、失败没提示"的低质量体验。一个健壮的取数流程至少要能分别渲染三态。
用函数组件 + Hooks 表达三个状态:
function UserView({ id }) { const [status, setStatus] = useState('loading'); const [data, setData] = useState(null); const [error, setError] = useState(null); const load = async () => { setStatus('loading'); try { const res = await fetch(`/api/user/${id}`); if (!res.ok) throw new Error('请求失败'); setData(await res.json()); setStatus('success'); } catch (e) { setError(e.message); setStatus('error'); } }; useEffect(() => { load(); }, [id]); if (status === 'loading') return <p>加载中…</p>; if (status === 'error') return <p>出错了:{error}</p>; return <div>{data.name}</div>; }
这里 status 唯一决定界面怎么显示,是"状态驱动视图"最直接的落地。load 在 useEffect 里按 id 触发,避免每次渲染都发请求。
<script setup> import { ref } from 'vue'; const status = ref('loading'); const data = ref(null); const error = ref(null); async function load() { status.value = 'loading'; try { const res = await fetch('/api/user/1'); data.value = await res.json(); status.value = 'success'; } catch (e) { error.value = e.message; status.value = 'error'; } } load(); </script> <template> <p v-if="status === 'loading'">加载中…</p> <p v-else-if="status === 'error'">出错了:{{ error }}</p> <p v-else>{{ data.name }}</p> </template>
结构完全相同:三态集中管理、模板按状态分支渲染。
请求该在哪触发?原则是"数据依赖谁就按谁触发"。组件挂载、数据源变化时应发请求;而卸载后不该再设状态(会被警告甚至闪烁)。清理的手段是把请求"标注为已取消"或对标志位判断,避免旧响应覆盖新状态。值得补一句的是:失败态别只停在"显示错误文字",最好带一个"重试"入口,把 load 再调一遍即可——一个能自愈的失败界面,比静态报错更接近合格产品。
竞态:用户快速切了两次页,第一次慢、第二次快,慢的先到却后落地,把新数据覆盖回旧数据。预防套路:记录"当前正在发的是哪一版",只接受最新版的响应,过期响应直接丢弃。
useEffect(() => { let cancelled = false; fetch(`/api/user/${id}`).then(async (res) => { if (!cancelled) setData(await res.json()); }); return () => { cancelled = true; }; // 清理时标记作废 }, [id]);
一句话心得:竞态源于"响应到的先后 ≠ 请求发出的先后",用取消标志收口。

评审一段取数逻辑,问三件事:有没有把三态都渲染出来?请求是不是在正确的时机触发且做了清理?并发旧响应会不会覆盖新值?这三个焦点齐了,取数代码一般就过了。而那些"发起即忘、失败白屏"的代码,业主往往是忽略了第一问。
到这里,"React、Vue 入门与组件化开发"的六章已经闭合:从框架演进选了方向,从通用原理懂了机制,分别在 React 与 Vue 里亲手写了组件,又在进阶章学会协作与复用,最后用状态管理与路由把多个页面组织成一个应用。你可以回头去对照导读那张知识地图,看看哪一格还想再走一遍。