Vue 3 中手动创建的 watch 实例必须主动调用返回的 stop 函数注销,否则引发内存泄漏;需在 onUnmounted 中统一调用,或按需提前终止、动态替换时先停后启。

Vue 3 中手动创建的 watch 实例,只要不是在组件 setup() 内**同步调用且未脱离生命周期管理**,就必须靠返回的 stop 函数主动注销,否则监听器会持续运行,持有响应式依赖、闭包变量甚至 DOM 引用,引发内存泄漏。
拿到 stop 函数并立即保存
每次调用 watch 或 watchEffect 都会返回一个函数,必须赋值给变量才能后续调用:
- 监听 ref:
const stop = watch(count, (val) => console.log(val)) - 监听 getter:
const stop = watch(() => state.name, (val) => {...}) - 监听 reactive 对象(自动 deep):
const stop = watch(state, () => {...}) - 监听多个源:
const stop = watch([a, b], ([newA, newB]) => {...})
在组件卸载前统一调用 stop
推荐使用 onUnmounted —— 它在组件真正从 DOM 移除、所有响应式连接即将断开时执行,时机最稳妥:
import { onUnmounted } from 'vue'onUnmounted(() => stop())- 若存在多个监听器,可统一存入数组或依次调用:
onUnmounted(() => { stop1(); stop2(); })
按条件提前终止监听
可以在回调内部安全调用 stop(),实现“一次性监听”或阈值控制:
立即学习“前端免费学习笔记(深入)”;
- 达到指定值后停止:
watch(count, (val) => { if (val >= 10) stop() }) - 配合异步操作,避免竞态:
watch(searchTerm, async (val) => { const controller = new AbortController(); onInvalidate(() => controller.abort()); await fetch(`/api?q=${val}`, { signal: controller.signal }) }) - 注意:不能在回调外层直接写
stop(),否则监听器尚未建立就已失效
动态替换监听器时先停后启
当监听逻辑随状态切换需重置(如表单类型变更、权限切换),应先调用旧 stop,再新建监听:
- 用
ref存储当前 stop 函数:const currentStop = ref() - 启动新监听前清理旧的:
if (currentStop.value) currentStop.value(); currentStop.value = watch(...) - 确保
onUnmounted中也调用一次:onUnmounted(() => currentStop.value?.())


















