Vue3 请求状态机:避免 loading、error 和 data 互相打架
背景 搜索页连续输入时,旧请求可能比新请求更晚返回。单独维护 loading、error、data,既容易组合出矛盾状态,也掩盖了响应乱序。 核心判断 用 idle、loading、success、error 表达互斥状态,空列表属于 success。 每次请求携带递增序号或取消信号,只有当前请求可以提交结果。 刷新旧数据时另行定义 refreshing 状态,明确是否保留旧内容。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 type State<T> = | { kind: "idle" } | { kind: "loading" } | { kind: "success"; data: T } | { kind: "error"; message: string }; const state = shallowRef<State<User[]>>({ kind: "idle" }); let sequence = 0; async function search(query: string) { const current = ++sequence; state.value = { kind: "loading" }; try { const data = await api.search(query); if (current === sequence) state.value = { kind: "success", data }; } catch (error) { if (current === sequence) { state.value = { kind: "error", message: String(error) }; } } } 边界与误区 状态类型只能约束组合,不能自动解决竞态。仅在 finally 里设置 loading=false,也可能让旧请求提前结束新请求的加载提示。 验证方式 用可控 Promise 让第二次请求先完成,确认第一次响应被丢弃;再覆盖初始、空结果、失败、取消和重试的界面表现。 小结 用有限状态表达 idle、loading、success、error,避免多个布尔值形成不可能组合。