6.3 异步数据请求管理


6.3 异步数据请求管理

本节摘要:前端把网络请求当成"三态状态机"来管:加载中、成功、失败。主角是请求编排(时机、清理、竞态),而不是某个库的魔法。本节用 fetch 与组件配合,讲清三态、请求时机与"请求竞态"这个最容易踩的坑,并给出可复用的处理模式。

本节阅读目标
阅读完本节,你应当能够:

  1. 把一次请求拆成 loading / success / error 三态并落到界面。
  2. 用生命周期或副作用钩子触发请求,并做卸载清理。
  3. 识别并发竞态并给出预防方法。

一、请求是一场三态状态机

请求不是"发起-拿到"两步,而是三种状态:发起(加载中)、成功(有数据)、失败(有错误)。界面若不区分它们,会呈现"点击没反应、失败没提示"的低质量体验。一个健壮的取数流程至少要能分别渲染三态。

二、React 侧的三态编排框架

用函数组件 + 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 触发,避免每次渲染都发请求。

三、Vue 侧的三态编排框架

<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]);

一句话心得:竞态源于"响应到的先后 ≠ 请求发出的先后",用取消标志收口

六、图:三态编排版面与清理

图:异步请求三态与竞态防护

图:异步请求三态与竞态防护

七、比稿要点

评审一段取数逻辑,问三件事:有没有把三态都渲染出来?请求是不是在正确的时机触发且做了清理?并发旧响应会不会覆盖新值?这三个焦点齐了,取数代码一般就过了。而那些"发起即忘、失败白屏"的代码,业主往往是忽略了第一问。

八、常见坑

  • 未做三态区分:loading 与 error 混在一起,失败也当成功处理。
  • 卸载后 setState:组件没了还想更新,条件用取消标志挡住。
  • 忽视竞态:慢响应覆盖快响应,交互明明点击新页却显示旧数据。

本节要点回顾

  • 三态状态机:loading / success / error 由独立 status 驱动界面。
  • 触发时机:挂载与数据源变化时发请求,卸载做清理。
  • 竞态防护:取消标志只收最新版,响应先后别依赖。
  • 评审三问:三态全?时机对?竞态挡?

到这里,"React、Vue 入门与组件化开发"的六章已经闭合:从框架演进选了方向,从通用原理懂了机制,分别在 React 与 Vue 里亲手写了组件,又在进阶章学会协作与复用,最后用状态管理与路由把多个页面组织成一个应用。你可以回头去对照导读那张知识地图,看看哪一格还想再走一遍。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U