watch 侦听器内异步异常需手动 try/catch 防御,不可依赖 Vue 错误边界;须用立即执行异步函数包裹 await,配合 onInvalidate 清理挂起请求,并提供错误降级反馈与状态恢复。

watch 侦听器内部无法自动捕获异步异常,一旦 await 后的 Promise 被 reject 或 fetch 抛错,且未手动处理,错误会直接冒泡到全局,严重时导致组件渲染中断甚至白屏。关键不是靠 Vue 的错误边界兜底,而是要在异步逻辑入口主动防御。
在 watch 回调里必须用 try/catch 包裹 await
watch 回调本身是同步注册的,不支持直接 async watch(..., async () => {...})。正确写法是在回调内部启动一个立即执行的异步函数,并用 try/catch 捕获其内部异常:
- 把异步逻辑写在
async () => {...}中,再立即调用 - 所有
await必须包裹在 try/catch 内,包括 API 请求、nextTick、router.push等可能失败的操作 - 捕获后应更新状态(如
loading.value = false、error.value = err.message),避免 UI 卡在 loading 或空白态
配合 onInvalidate 清理挂起请求,防止状态污染
用户频繁变更监听源(如快速输入搜索词)时,上一个请求可能还在 pending,此时新请求已发起。若旧请求 later 返回并覆盖新结果,就会出现 UI 错乱或白屏。需借助 onInvalidate 主动中断:
- 在
watchEffect或带onInvalidate的watch中使用AbortController - 每次请求前生成新 signal,并传入
fetch或 axios 的signal选项 - 在
onInvalidate中调用controller.abort(),确保旧请求不再影响后续状态 - 也可用布尔标记位(如
let isAlive = true),在 then/catch 前检查是否仍有效
错误发生后要有降级反馈,不能静默失败
即使捕获了异常,若 UI 不提示、不恢复、不重置状态,用户会感知为卡死或白屏。需要明确的错误响应路径:
- 设置专门的 error ref(如
const apiError = ref('')),赋值后模板中可显示友好提示 - 重置 loading 状态,避免骨架屏无限等待
- 对关键操作(如表单提交、路由跳转)失败时,考虑提供重试按钮或自动 fallback 行为
- 生产环境建议将错误摘要(脱敏后)上报监控系统,便于定位高频崩溃点
补充:Vue 错误边界不覆盖 watch 异步错误
errorCaptured 只拦截后代组件同步抛出的错误;Vue.config.errorHandler 能兜住 watch 中同步代码的错误,但对 await 后未 catch 的 reject 无效——它属于原生 Promise rejection,需靠 window.addEventListener('unhandledrejection') 补位。所以依赖外部兜底不如自己守好第一道门。


















