前言(什么是effectScope)

effectScopevue3引入的一个功能,它是响应式系统的一部分,可以用来在vuecomposition API中管理副作用(effects),通过effectScope,我们可以更好地控制和管理副作用,特别是在构建大型的应用的使用,这能够提高代码的组织性和可维护性!
😕 那么什么是副作用?
👉vue中,副作用(effects)主要是指响应式依赖的注册过程,比如,当使用computed或者watch等“监听性质”的API时,vue内部会建立起一个响应式系统,自动跟踪相关依赖,并在依赖变化时重新执行这些副作用,以保证数据与视图的同步!
这里我将effectScope称之为一种“自动的可管理的垃圾(依赖)处理容器”

为什么要使用effectScope

在没有effectScope之前,vue实例本身应该会跟踪这些副作用,然后在组件销毁(unmounted)的时候,自动销毁这些副作用的,但是随着composition API的使用越来越广泛,应用的结构也变得越来越灵活,原有的副作用跟踪机制在某些场景下显得不够灵活和直观,或者用起来不够顺畅,因此,使用effectScope来解决这个问题,它允许开发者显示地组织和管理副作用

如何使用effectScope

1
2
3
4
5
6
7
8
9
10
11
import { ref, effectScope } from 'vue'
// 创建一个副作用处理容器scope
const scope = effectScope()
scope.run(() => {
const count = ref(0)
watch(count, newVal => {
constole.info(newVal)
})
})
// 在需要的时候对这个副作用进行管理
scope.stop() // 停止容器内的所有副作用

这里我们通过effectScope()来创建一个scope容器对象,然后通过其run()方法将所有的副作用在run()方法的参数中来实现!这里我们针对count变量创建了一个watch副作用,然后在需要销毁副作用的时候通过调用这个scope.stop()方法来销毁容器内的所有副作用!

从上面我们可以看出关于通过effectScope()方法所创建出来的EffectScope实例对象拥有runstop方法,其数据结构如下

1
2
3
4
interface EffectScope {
run<T>(fn: () => T): T | undefined;
stop(): void
}

🌟 关于这个effectScope(detached: boolean)这个API中,还提供了一个boolean参数detached,该参数为true时代表一个被嵌套的scope不可以被其父scope收集,当父scope被销毁的时候,这个scope将不会被连带stop,只有显示地调用这个scopestop()才被正常销毁!

🌟 而且在vue3中除了effectScope()API之外,还提供了另外两个API:

  1. getCurrentScope(): 获取当前scope;
  2. onScopeDispose(fn: () => void): void: 当当前的scope被销毁的时候,自动执行的回调方法!

🌟 在组件component内部通过onUnmounted()来销毁资源的,那么对于在非组件层面(比如composable function)中,所创建出来的响应式副作用则可以通过onScopeDispose()来进行管理销毁回调操作,如下所示:

1
2
3
4
5
6
7
8
import { onScopeDispose } from 'vue'
const scope = effectScope()
scope.run(() => {
onScopeDispose(() => {
console.info('已经完成销毁操作了!')
})
})
scope.stop()

可使用effectScope的场景

vue3setup本身就提供了自动副作用管理的机制,一般在setup中创建的响应式引用(ref或者reactive)以及副作用(如watchcomputed)等注册的,都会被vue自动跟踪和管理,当组件卸载时,这些副作用也会被自动清理,无需开发者接入!
🫣 这一点,vue已经帮开发者做得足够好了, 😕 那么关于effectScope的使用场景是什么呢?主要有 👇 场景

组合式函数的重用逻辑

在某些复杂的场景下,可能需要更加精细化地来控制副作用的激活与停止,而不仅仅是绑定到组件的生命周期上!
比如 🈶 这么的一个功能,当用户完成某些操作之后,需要启动一个副作用来进行数据的实时更新,但这个功能可能需要根据用户的操作来多次开启与关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// realTimeUpdates.js
import { ref, effectScope, watchEffect } from 'vue'
const useRealTimeUpdates = () => {
const isEnabled = ref(false)
const scope = effectScope()
const startUpdates = () => {
if(!isEnabled.value){
isEnabled.value = true
scope.run(() => {
watchEffect(() => {
console.info('实时更新数据...')
})
})
}
}
const stopUpdates = () => {
if(isEnabled.value){
isEnabled.value = false
scope.stop()
}
}
return { startUpdates, stopUpdates }
}

🥸 effectScope为我们提供了一种方便的方式来精细化地管理副作用,无论是在组件内部还是在全局环境或者可重用逻辑中,借助于effectScope,可以更好地控制程序行为,确保资源被适时地释放,从而提供应用的性能和可维护性!