effectscope 用于统一管理副作用生命周期,需在 scope.run() 内定义 watch、computed 等,组件卸载时调用 scope.stop() 批量关停并触发 onscopedispose 清理;封装进组合式函数可实现“自带销毁”,但副作用必须写在 run 内且需主动调用 stop。

在 setup 中用 effectScope 统一管理多个副作用生命周期,关键不是“额外加一层”,而是把原本散落在 onMounted/onUnmounted 里的清理逻辑,收束到一个可控制的容器里——让 watch、computed、定时器、事件监听等自动归属、批量关停。
创建作用域并立即运行副作用
所有需要被统一管理的响应式操作,必须写在 scope.run() 回调内。Vue 会自动将其中创建的 watch、watchEffect、computed 等注册到该作用域:
- const scope = effectScope()
- scope.run(() => {
watch(a, () => {...})
const b = computed(() => a.value * 2)
const timer = setInterval(() => {...}, 1000)
onScopeDispose(() => clearInterval(timer))
})
绑定组件卸载时机停止整个作用域
组件销毁时调用 scope.stop(),就能一次性关停所有已注册的副作用:
- onUnmounted(() => scope.stop())
- stop 后:watch 不再响应变化、computed 停止更新、watchEffect 彻底静默
- 同时触发所有 onScopeDispose 注册的清理函数(如 clearTimeout、removeEventListener)
封装进组合式函数,实现“自带销毁”能力
把 effectScope 写进 useXxx 函数内部,调用方无需关心清理细节:
- 函数内创建 scope,并在 run 中定义副作用和 onScopeDispose 清理项
- 返回一个 stop 方法,或直接在 onUnmounted 中调用 scope.stop()
- 例如 useWebSocket 封装连接 + 心跳 + 错误重试,全部由 scope 统一兜底
避免常见错误
这些点不注意,effectScope 就形同虚设:
- 副作用写在 scope.run 外 → 不会被收集,stop() 对它无效
- 在 setup 外(比如事件回调)调用 scope.run → 可能因上下文丢失导致绑定失败
- 误以为 stop() 会销毁 ref/reactive 数据 → 实际只停副作用,数据仍可读写
- 组件卸载时不调用 scope.stop() → 即使用了 effectScope,也等于没用










