一起学习Vue,关注我,不迷路~
在Vue的setup中,响应会在开始初始化的时候被收集,在实例被卸载的时候,响应就会自动的被取消追踪了,这时一个很方便的特性。
但是,当我们在组件外使用或者编写一个独立的包时,这会变得非常麻烦。当在单独的文件中,我们该如何停止computed & watch的响应式依赖呢?
//(vue-RFC示例代码)
const disposables = []
const counter = ref(0)
const doubled = computed(() => counter.value * 2)
disposables.push(() => stop(doubled.effect))
const stopWatch1 = watchEffect(() => {
console.log(`counter: ${counter.value}`)
})
disposables.push(stopWatch1)
const stopWatch2 = watch(doubled, () => {
console.log(doubled.value)
})
disposables.push(stopWatch2)
代码解读:
上面的代码中,我们写了一共三个计算属性,把这些就算属性的stopHandle都存到一个数组中,意思是我们需要维护这个数组,这样将来在需要的时候,就可以像下面这样,直接把所有的响应都停掉:
//(vue-RFC示例代码)
disposables.forEach((f) => f())
disposables = []
上面我们调用了数组中的每一个stopHandle,停止了所有的响应。
但是这样我们每定义一个计算属性,都需要加入到这个数组中,是不是很麻烦?
所以,在Vue3.2中,新的API-----Effect Scope API出现了,专门用来解决这个问题。
首先,effectScope是一个函数,调用effectScope函数会返回一个对象,其中包含了run(一个函数)和stop(一个函数);
//(vue-RFC示例代码)
function effectScope(detached?: boolean): EffectScope
interface EffectScope {
run<T>(fn: () => T): T | undefined // undefined if scope is inactive
stop(): void
}
我们可以像下面这样使用它。(vue-RFC示例代码)
//(vue-RFC示例代码)
const scope = effectScope()
scope.run(() => {
const doubled = computed(() => counter.value * 2)
watch(doubled, () => console.log(doubled.value))
watchEffect(() => console.log('Count: ', doubled.value))
})
// to dispose all effects in the scope
scope.stop()
这样,在run中定义的所有计算属性,在调用了scope对象的stop()方法之后,所有的依赖都被停止了。
做个实验:
const scope = effectScope()
let counter = ref(0)
setInterval(() => {
counter.value++
}, 1000);
scope.run(() => {
watchEffect(() =>console.log(`counter: ${counter.value}`))
})
/*log:
counter: 0
counter: 1
counter: 2
counter: 3
counter: 4
counter: 5
*/
我们不停止,浏览器会一直输出conter的值:
const scope = effectScope()
let counter = ref(0)
setInterval(() => {
counter.value++
}, 1000);
scope.run(() => {
watchEffect(() =>console.log(`counter: ${counter.value}`))
})
scope.stop()
/*log:
counter: 0
*/
但当我们调用了stop之后,浏览器只会输出一次。