watcheffect 中用 oncleanup 清理过期定时任务:每次执行前自动清除上轮定时器,防止堆积;oncleanup 必须同步调用,用闭包变量存 timer 更安全;典型用于防抖和轮询场景。

在 watchEffect 中用 onCleanup 清理过期定时任务,核心是:每次副作用重新执行前,自动清除上一轮未完成的定时器,防止多个定时器堆积导致卡顿或状态错乱。
必须同步注册 onCleanup
onCleanup 只能在 watchEffect 回调的同步执行阶段调用,不能放在 await、setTimeout 或事件回调里。否则 Vue 无法绑定清理逻辑,会报错 “no active effect found”。
- ✅ 正确:在
setInterval或setTimeout启动前,立刻调用onCleanup(() => clearTimeout(timer)) - ❌ 错误:
setTimeout(() => { onCleanup(...) }, 100)—— 此时已脱离响应式上下文
推荐用闭包局部变量存 timer
不用 ref 管理定时器 ID,直接在回调内声明局部变量,靠闭包保持引用,更安全、无污染、无需判空。
- 每次
watchEffect执行都是独立作用域,timer是当前轮次专属 -
onCleanup能直接访问该闭包内的timer,清理精准 - 组件卸载后,变量自然被垃圾回收,配合
onCleanup形成双重保障
典型防抖场景写法
比如搜索框输入后延迟 500ms 发起请求:
const keyword = ref('')<br>watchEffect((onCleanup) => {<br> const timer = setTimeout(() => {<br> console.log('发起请求:', keyword.value)<br> // api.search(keyword.value)<br> }, 500)<br> onCleanup(() => clearTimeout(timer))<br>)
用户连输 “abc”,会触发三次 watchEffect 执行:第一次启动 timer A,第二次立即清除 A 并启动 B,第三次清除 B 并启动 C —— 最终只执行 C 对应的请求。
轮询类定时任务处理
若需长期运行的 setInterval(如每 3 秒拉取状态),仍要遵循同一原则:
- 每次进入
watchEffect第一件事就是清除旧定时器(即使还没创建,也确保无残留) - 根据响应式开关(如
isActive.value)决定是否新建setInterval -
onCleanup中再次清除,并置空逻辑可选(闭包变量无需置空)
这样可保证任意时刻最多只有一个有效定时器,避免 CPU 占用飙升。










