问题场景
在Vue3项目中使用组合式函数(Composable)封装副作用逻辑时,组件卸载后副作用仍在执行。典型代码如下:
export function usePolling(fetchFn, interval = 3000) {
const data = ref(null);
let timer = setInterval(() => {
fetchFn().then(res => data.value = res);
}, interval);
onScopeDispose(() => clearInterval(timer));
return { data };
}
在组件中使用这个Composable,切换路由后组件卸载,但setInterval没有被清除,定时请求仍在持续发送。控制台也没有报错,问题很难定位。
根因:活跃的EffectScope未销毁
onScopeDispose的触发条件是当前EffectScope被销毁。组件的setup()函数默认运行在组件的EffectScope中,组件卸载时该scope会被自动销毁,onScopeDispose回调正常执行。
问题出在异步调用场景。如果Composable在async setup()中使用,或者在setTimeout/Promise.then的回调中调用,此时已经脱离了组件的EffectScope:getCurrentScope()返回null,onScopeDispose注册的回调实际上无处挂载。这意味着组件卸载时,没有scope来触发这个cleanup。
另一种常见场景是在Pinia Store的action中调用Composable。Pinia的action不在组件的EffectScope内执行,onScopeDispose同样不会触发。
解决方案:显式管理副作用生命周期
方案一:返回cleanup函数,由调用方手动执行
export function usePolling(fetchFn, interval = 3000) {
const data = ref(null);
let timer = setInterval(() => {
fetchFn().then(res => data.value = res);
}, interval);
const stop = () => {
clearInterval(timer);
timer = null;
};
if (getCurrentScope()) {
onScopeDispose(stop);
}
return { data, stop };
}
组件中使用onUnmounted调用stop(),双重保险。
方案二:用watchEffect替代setInterval
把轮询逻辑改为watchEffect+手动递归setTimeout的方式,watchEffect会在scope销毁时自动停止。不过这种方式实现轮询需要额外处理首次执行和重试逻辑,代码复杂度增加。
方案三:封装通用的副作用收集器
export function useCleanup() {
const effects = [];
const add = (fn) => effects.push(fn);
const run = () => effects.forEach(fn => fn());
if (getCurrentScope()) onScopeDispose(run);
return { add, run };
}
Composable内部用cleanup.add(() => clearInterval(timer))注册,组件卸载时调用cleanup.run()。这种方式适合一个组件内使用多个Composable的场景,统一管理所有副作用的清理。
排查响应式泄漏的调试方法
Vue DevTools的组件面板可以看到每个组件的EffectScope数量。如果一个组件卸载后其scope内的watcher或computed仍在运行,DevTools会在组件面板中显示已卸载组件的残留副作用。
更直接的方式是用Chrome DevTools的Performance面板录制一段时间线,切到对应路由后再切回,观察内存曲线是否持续上升。如果上升,在Memory面板中拍快照,搜索ReactiveEffect对象数量,数量不减说明有响应式泄漏。
预防措施
在项目规范中约定:所有Composable必须返回stop或dispose方法,不依赖onScopeDispose作为唯一的cleanup机制。ESLint可以写一条自定义规则,检测Composable中使用了onScopeDispose但没有导出cleanup函数的情况,在CI阶段拦截这类问题。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-han-shu-zhong-de-xiang-ying-shi-xie-lou/