在 watcheffect 中处理异步失败需手动 try-catch 包裹请求,finally 统一重置 loading,非 aborterror 才设 error.value,并配合 oninvalidate 清理旧请求,模板响应 error、loading、data 实现降级展示。

在 watchEffect 中处理异步加载失败,关键不是“捕获异常”本身,而是主动控制错误状态、避免竞态残留、并提供可感知的降级反馈。它不依赖自动兜底,而靠你显式定义 loading、error、data 三者联动的响应式流程。
必须用 try-catch 包裹异步逻辑
watchEffect 不会自动捕获 Promise rejection 或同步抛错,未处理的异常会冒泡中断副作用执行,甚至导致清理逻辑(如 onInvalidate)失效。所有异步调用都应在函数体内手动包裹:
- fetch / axios 请求必须放在
try块中,catch捕获网络错误、HTTP 非 2xx、AbortError 等 - 避免只写
.catch()而不用try/catch—— 后者能统一处理同步异常(如 URL 拼接出错)和异步拒绝 - 在
catch中区分错误类型:非AbortError才设error.value,防止取消请求误报失败
loading 状态要收口到 finally
loading 卡住的主因是 isLoading.value = false 没覆盖所有退出路径。正确做法是:
- 请求开始时立刻设
isLoading.value = true -
所有分支出口都必须重置 loading:成功、失败、被取消、甚至未进入
try的早期 return - 统一写在
finally块里最安全,确保无论发生什么,loading 都能归零
错误状态需驱动 UI 降级展示
仅设置 error.value 不够,模板必须响应它来切换内容:
- 定义
const error = ref(null)和const data = ref(null),二者均为响应式 - 模板中用
v-if="error"显示错误提示,v-else-if="!isLoading"渲染数据,v-else显示 loading 骨架 - 可加“重试按钮”,点击时清空
error.value并触发一次watchEffect重跑(例如通过修改一个无关 ref 触发依赖更新)
配合 onInvalidate 防止旧请求干扰
用户快速切换参数时,上一个请求可能还在 pending,返回后覆盖新结果。降级方案需包含清理机制:
- 每次执行前创建
AbortController,把signal传给fetch - 用
onInvalidate注册清理函数:onInvalidate(() => controller.abort()) -
catch中检查err.name === 'AbortError',跳过设 error,避免误提示“加载失败”











