跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

如何利用 watchEffect 监听清理定时器?防止页面卡顿的进阶实战教程

watchEffect 需手动清理定时器以避免内存泄漏和卡顿,通过 onInvalidate 在下次执行前清除上一个定时器;推荐用闭包局部变量而非 ref 存储 timer,结合 flush: 'post' 实现防抖轮询。 在 Vue 3 中,
watchEffect
本身不直接“监听定时器”,而是通过响应式依赖自动追踪副作用中的 reactive 数据或 ref 值。清理定时器的关键在于:**在副作用重新执行前,主动清除上一次启动的定时器**,避免多个定时器累积导致内存泄漏或页面卡顿。 为什么需要手动清理定时器? 如果你在
watchEffect
内部用
setInterval
或
setTimeout
,而没做清理,每次响应式数据变化都会新建一个定时器——旧的还在跑,新的又来了。久而久之,几十甚至上百个定时器同时执行,CPU 占用飙升,页面明显卡顿。 watchEffect 的清理函数怎么用?
watchEffect
接收一个回调函数,该函数可选地接收一个 onInvalidate 参数(常简写为
onInvalidate
或
cleanup
),它会在本次副作用被覆盖(即下一次执行前)自动调用,适合用来清除定时器、取消请求、解绑事件等。 用法示例:
const timer = ref(null) watchEffect((onInvalidate) => { // 每次执行前,先清理上一次的定时器 onInvalidate(() => { if (timer.value) { clearInterval(timer.value) timer.value = null } }) // 启动新定时器 timer.value = setInterval(() => { console.log('定时任务执行中...') }, 1000) })
更安全的写法:用局部变量替代 ref 存储定时器 其实不需要把定时器存在
ref
里。只要在
onInvalidate
中能访问到当前定时器实例即可。推荐用闭包局部变量,更简洁、无副作用:
watchEffect((onInvalidate) => { let timer = setInterval(() => { console.log('执行中...') }, 1000) onInvalidate(() => { clearInterval(timer) }) })
每次
watchEffect
执行,都会生成新的
timer
变量
onInvalidate
闭包捕获了本次的
timer
,确保清理的是“自己启动的那个” 无需管理 ref 状态,逻辑更干净,也不怕重复清理 实战场景:搜索框防抖 + 定时轮询状态 比如用户输入关键词后,每 2 秒检查一次后台处理进度。既要防抖输入,又要定时轮询,且切换关键词时必须停掉旧轮询:
const searchKey = ref('') const status = ref('idle') watchEffect((onInvalidate) => { if (!searchKey.value.trim()) return let count = 0 const timer = setInterval(() => { count++ console.log(`正在轮询第 ${count} 次...`) // 这里调用 API 获取 status // fetchStatus(searchKey.value).then(res => status.value = res.status) }, 2000) onInvalidate(() => { clearInterval(timer) }) }, { flush: 'post' // 确保 DOM 更新后执行,避免竞态 })
flush: 'post'
让副作用在组件更新后运行,更适合依赖最新 DOM 或 ref 值的场景 输入变化 → 上次
onInvalidate
触发 → 清除旧定时器 → 启动新定时器 哪怕用户狂敲键盘,也只会有一个活跃轮询,彻底杜绝卡顿

相关文章