选项卡切换与 Loading 状态封装
在业务页面中,选项卡切换是高频交互场景。例如切换商品分类时,通常需要携带对应 id 重新请求数据。
如果不做统一封装,常见问题包括:
- 每个页面重复编写请求逻辑;
loading/success/error三态实现不一致;- 重试逻辑分散,维护成本高。
因此,更推荐将该能力抽象为统一的 useFetch 方案。
方案目标
统一处理以下状态:
loading:请求进行中;success:请求成功并渲染结果;error:请求失败并支持手动重试。
解析示意图:

实现思路(Vue)
核心点是监听“请求依赖”的变化(如 id 或 url),并自动触发请求:
- 用
computed维护派生请求地址; - 用
watchEffect自动追踪依赖并执行请求; - 用
unref兼容ref与普通值参数。
依赖监听示例
import { computed, ref, watchEffect } from "vue";
const id = ref(1);
const changeId = () => id.value++;
const url = computed(() => `https://api-xxxxx.com?id=${id.value}`);
watchEffect(() => {
console.log("watchEffect url:", url.value);
});
说明:
- 每次执行
changeId,id更新后url会重新计算; watchEffect会自动监听依赖并立即执行;- 若需要拿到“新旧值”,应使用
watch而不是watchEffect。
useFetch 封装示例
import { isRef, ref, unref, watchEffect } from "vue";
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(false);
async function doFetch() {
data.value = null;
error.value = null;
loading.value = true;
try {
const urlVal = unref(url);
const res = await fetch(urlVal);
data.value = await res.json();
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
}
if (isRef(url)) {
watchEffect(doFetch);
} else {
doFetch();
}
return { data, error, loading, doFetch };
}
页面渲染建议
建议将状态渲染统一为公共组件,避免页面重复判断逻辑:
<div v-if="loading">loading...</div>
<div v-else-if="error">错误:xxxx(可重试)</div>
<div v-else>数据:xxxx</div>
在企业项目中,这类基础能力统一后,能够显著降低重复代码并提升交互一致性。
与手动实现的对比
| 维度 | 页面内手写请求逻辑 | 统一 useFetch 封装 |
|---|---|---|
| 状态管理 | 每个页面各自定义 loading/error 变量 | 由封装统一返回,命名一致 |
| 依赖切换 | 需要手动在切换回调里重新发请求 | watchEffect 自动感知依赖变化 |
| 重试逻辑 | 容易遗漏或各页面实现不一致 | 复用同一套 doFetch,重试即重新调用 |
| 可测试性 | 逻辑分散,难以单独测试 | 可对 useFetch 单独编写单元测试 |
回归检查清单
- 切换选项卡(更新依赖
id)后能正确重新发起请求,且旧请求结果不会覆盖新请求结果; - 请求失败时
error状态正确展示,重试按钮可正常触发doFetch重新请求; - 快速连续切换选项卡时不出现请求堆积或界面闪烁「loading -> 数据 -> loading」的异常跳变;
-
url传入非ref的普通值时(一次性请求场景),逻辑仍能正确执行且不会误触发watchEffect; - 组件销毁时无残留的未处理 Promise 导致的报错或内存泄漏提示。
常见疑问
- 为什么不直接用
watch而用watchEffect?watchEffect会自动收集依赖,不需要手动列出监听的字段,对于「依赖变化即整体重新请求」的场景更简洁;如果后续需要对比新旧值做差异化处理,再改为watch也不冲突。 useFetch是否可以扩展支持缓存?
可以,常见做法是按url作为 key 缓存最近一次结果,命中缓存时先展示旧数据再静默更新,避免每次切换都出现短暂空白,具体策略需结合业务对「数据实时性」的要求取舍。
相关文章
GridView 宫格加载渲染优化
系统首页 GridView 宫格模块接口耗时不高,但首次进入总耗时接近 22s。复盘耗时分层定位过程、代码层面的瓶颈(约 2000 行、100 个 tab 重复节点)、优化手段与最终指标对比。
新商家系统性能优化实践
商家系统新版本上线后,团队持续针对构建效率与用户体验做了一轮系统性优化。本文记录核心方案与落地结果,供后续版本复用。
老系统升级与兼容改造实践
公司原有业务系统采用 Node + jQuery 架构,长期运行后暴露出典型遗留系统问题:
防篡改水印
在各类管理后台、SaaS 平台或内部系统中,页面水印已经是非常常见的能力:一方面在截图时携带账号、姓名、时间等信息,降低截图外传的风险;另一方面在上传图片或文档预览时,通过水印标记上传者与时间,满足审计和合规需求。
企业微信 uni-app H5:OAuth 回退白屏与列表缓存
第三方 BI 报表项目,企业微信内嵌 H5,uni-app 编译,history 模式。主页面 BiLink 同时承担静默授权和列表展示,上线后碰到两个问题:授权完清掉 URL 参数,iOS 侧滑返回还是白屏;列表加了缓存以后,刷新行…
内部插件 / 工具库开发实践
在企业前端项目中,utils、hooks、libs 这类通用能力通常分散在多个仓库,常见问题包括:
Series
team documents
14 / 22