effectscope是vue 3.2+提供的副作用容器,通过scope.run()注册、scope.stop()一键清理watch等响应式逻辑;需在run内定义副作用才能被收集,stop会彻底释放所有关联资源并助gc回收。

effectScope 是 Vue 3.2 引入的副作用生命周期管理机制,本质是一个可控制、可销毁的“响应式逻辑容器”。它不改变数据本身,而是为 watch、computed、watchEffect 等响应式操作划定明确的归属边界,让清理行为从“逐个手动 stop”变成“一键关停”。
作用域怎么绑定副作用?
副作用必须在 scope.run(() => { ... }) 内定义,才会被自动注册进该作用域。Vue 内部通过一个活跃作用域栈(active effect scope stack)实现隐式绑定——只要当前有活跃作用域,所有响应式 API 调用都会自动挂载进去。
- 写在 run 外面的 watch 或 computed,不会被收集,调用 stop 也无效
- setup() 中写的响应式逻辑,默认就运行在组件隐式创建的作用域里
- 异步回调中(如 setTimeout、Promise.then)执行的副作用,需确保作用域仍处于活跃状态,否则可能漏注册
stop() 到底做了什么?
调用 scope.stop() 不是简单地“暂停”,而是触发一次彻底的释放流程:
- 遍历作用域内所有已注册的响应式副作用,依次执行其内部清理函数
- 断开 computed 的依赖追踪链,使其不再更新
- 停止 watch 和 watchEffect 的响应监听,后续数据变化不再触发回调
- 清空 track/trigger 关系中的引用,将内部指针置为 null,协助垃圾回收(GC)
- 该操作不可逆,stop 后作用域永久失效,不能再 run 新逻辑
嵌套与独立:两种关键模式
作用域支持层级结构,也支持脱离上下文:
- 嵌套作用域:子作用域默认继承父作用域的激活状态;父 scope.stop() 时,所有子作用域自动销毁,适合模块化分层(如“数据加载 + UI 动画 + 错误重试”)
- 独立作用域(detached):用 effectScope({ detached: true }) 创建,不加入当前活跃栈,也不受父作用域影响;适用于全局长期存在的逻辑(如 WebSocket 监听、主题切换响应)
怎么避免常见踩坑?
effectScope 很强大,但容易因使用不当而失效:
- 组件卸载时忘记调用 stop → 即使用了作用域,照样内存泄漏
- 把定时器、事件监听器等非 Vue 原生副作用直接写在 run 里 → 它们不会被自动清理
- 正确做法是配合 onScopeDispose 注册自定义清理逻辑,比如关闭 WebSocket、清除 setInterval、移除原生事件监听
- 不要暴露原始 scope 对象给调用方;封装成组合式函数时,应返回 start/stop 方法或直接绑定 onBeforeUnmount
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











