Vue3 effectScope管理副作用的创建与销毁
在 Vue 3 的组合式 API 中,watch、watchEffect 和 computed 等响应式 API 会产生“副作用”。在组件内部,Vue 会在组件卸载时自动清理这些副作用。但在组件外部(如在工具函数、插件或独立的逻辑复用模块中)使用这些 API 时,必须手动停止它们以避免内存泄漏。effectScope API 正是为了解决这一痛点,它提供了一个机制来将多个副作用聚合在一起,实现统一创建和统一销毁。
创建作用域并收集副作用
使用 effectScope 可以创建一个独立的作用域对象,所有在该作用域 run 方法中创建的副作用都会被该作用域自动捕获。
- 打开 你的 Vue 3 项目文件,找到需要处理响应式逻辑的脚本区域。
- 引入
effectScope、ref和watch等必要的 API。
import { effectScope, ref, watch, computed } from 'vue'
- 调用
effectScope()函数来创建一个作用域实例。为了演示,我们将其赋值给scope变量。
const scope = effectScope()
- 执行
scope.run()方法。传入 一个箭头函数作为参数,并将所有需要被管理的响应式逻辑(如watch或computed)写在这个函数内部。
scope.run(() => {
const count = ref(0)
const doubled = computed(() => count.value * 2)
// 这个 watch 会被当前 scope 自动捕获
watch(count, (newValue) => {
console.log(`Count changed to: ${newValue}`)
})
// 模拟外部访问,用于测试
window.triggerChange = () => {
count.value++
}
})
在上述代码中,watch 监听器和 computed 计算属性都被 scope 实例记录了下来。
统一销毁副作用
当不再需要这些响应式逻辑时(例如模块被卸载或特定功能结束),可以通过调用作用域实例的 stop 方法一次性清理所有被收集的副作用。
- 调用
scope.stop()方法。
// 当满足某个条件时(例如用户点击了停止按钮,或组件卸载)
scope.stop()
- 验证 销毁效果。在调用
stop后,尝试修改count的值(例如在控制台执行window.triggerChange()),你会发现控制台不再输出日志,说明watch监听器已被成功移除。
处理嵌套作用域
effectScope 支持嵌套使用。父作用域停止时,所有子作用域也会自动停止。这对于构建具有层级关系的插件或复杂逻辑非常有用。
- 创建 一个主作用域
mainScope。 - 在
mainScope.run()内部,创建 子作用域childScope。
const mainScope = effectScope()
mainScope.run(() => {
const mainState = ref(true)
watch(mainState, (val) => console.log('Main scope:', val))
// 创建嵌套子作用域
const childScope = effectScope()
childScope.run(() => {
const childState = ref(100)
watch(childState, (val) => console.log('Child scope:', val))
})
})
- 调用
mainScope.stop()。此时,主作用域内的watch以及子作用域childScope内的watch都会同时被销毁,无需单独停止子作用域。
注册清理回调
除了自动处理响应式 API 的副作用,effectScope 还允许你注册特定的清理回调函数(类似于组件的 onUnmounted),以便在作用域停止时执行自定义的收尾逻辑(如清除定时器、移除事件监听器)。
- 引入
onScopeDispose。 - 在
scope.run()内部,调用onScopeDispose并传入一个包含清理逻辑的函数。
import { effectScope, onScopeDispose } from 'vue'
const scope = effectScope()
scope.run(() => {
const timer = setInterval(() => {
console.log('Timer is running...')
}, 1000)
// 注册清理回调
onScopeDispose(() => {
console.log('Cleaning up timer...')
clearInterval(timer)
})
})
// 当调用 scope.stop() 时,上面的回调函数会自动执行
这种机制确保了任何与该作用域生命周期相关的资源都能被正确释放。
在非组件环境中的实战应用
effectScope 最典型的应用场景是编写 Vue 插件或全局状态管理库(如 Pinia)。下面展示如何在独立模块中管理全局状态。
- 定义 一个全局变量
globalScope。 - 编写 一个初始化函数
setupGlobalState,在其中创建作用域并建立响应式连接。 - 编写 一个销毁函数
disposeGlobalState,用于在应用卸载时清理资源。
import { effectScope, ref, watch } from 'vue'
let globalScope
export function setupGlobalState() {
// 创建全局作用域
globalScope = effectScope(true) // 参数 true 表示这是一个分离的作用域,通常用于全局单例
globalScope.run(() => {
const globalCounter = ref(0)
// 监听全局状态,可能用于同步到 LocalStorage
watch(globalCounter, (val) => {
localStorage.setItem('counter', val)
})
// 暴露给外部操作
window.globalCounter = globalCounter
})
}
export function disposeGlobalState() {
if (globalScope) {
// 一行代码清理所有全局副作用
globalScope.stop()
}
}
- 在 应用入口处 调用
setupGlobalState()。 - 在 应用销毁前(如果需要)调用
disposeGlobalState()。

暂无评论,快来抢沙发吧!