跳到主要内容

认识

2024年03月20日
柏拉文
越努力,越幸运

一、认识


effectScope 创建一个 effect 作用域,可以捕获其中所创建的响应式副作用 (即计算属性和侦听器),这样捕获到的副作用可以一起处理。开发者自己来收集副作用成本较高, 可能也会忘记收集,造成内存泄漏等。因此, 提供了一个 effectScope 帮助我们收集、清理副作用。

1.1 手动收集

import { ref,stop,computed, watch, watchEffect } from 'vue';

let disposables = [];
const count = ref(0);

const doubleCount = computed(()=> count.value * 2);
disposables.push(()=> stop(doubleCount));

const watchCount = watch(()=> count.value, ()=> console.log("watch count", count.value));
disposables.push(watchCount);

const watchEffectCout = watchEffect(()=> console.log('watchEffect count', count.value));
disposables.push(watchEffectCout);

const watchDoubleCount = watch(()=> doubleCount.value, ()=> console.log("watch doubleCount", doubleCount.value));
disposables.push(watchDoubleCount);

const watchEffectDoubleCount = watchEffect(()=> console.log('watchEffect doubleCount', doubleCount.value));
disposables.push(watchEffectDoubleCount);

setTimeout(()=>{
count.value++;
},2000);

setTimeout(()=>{
disposables.forEach(dispose=> dispose());
disposables = []
},4000);

setTimeout(()=>{
count.value++;
}, 60000);

二、语法


2.1 单层 effectScope

const counter = ref(2);
const scope = effectScope();
scope.run(() => {
const doubled = computed(() => counter.value * 2);
watch(doubled, () => console.log("watch:", doubled.value));
watchEffect(() => console.log("watchEffect:", doubled.value));
});

setTimeout(() => {
counter.value = 3; // 更改 counter 的值,会触发 watch 和 watchEffect
}, 1000);

setTimeout(() => {
scope.stop(); // 停止掉 scope 作用域内的所有 effect 副作用
}, 3000);

setTimeout(() => {
counter.value = 5; // 再次更改 counter 的值,但是由于 scope 已经停止,所以不会触发 watch 和 watchEffect
}, 5000);

2.2 子父 effectScope

effectScope(detached)detached 表示是否阻断和父级的联系, 若为 true 则表示与父级断开关联,执行父级stop方法时会递归停止子集的监听,但子集detachedtrue时则不会停止。

const counter = ref(2);

let nestedScope;
let childScope;
const parentScope = effectScope(); // 创建 parentScope 作用域

parentScope.run(() => {
const doubled = computed(() => counter.value * 2);

nestedScope = effectScope(true); // 创建 nestedScope 作用域, 但是与父级断开连接, 不会受到父级的影响
nestedScope.run(() => {
watch(counter, () => console.log("nestedScope watch:", counter.value));
watch(doubled, () => console.log("nestedScope watch:", doubled.value));
watchEffect(() => console.log("nestedScope watchEffect:", doubled.value));
});

childScope = effectScope(); // 创建 childScope 作用域, 与父级连接, 会受到父级的影响
childScope.run(() => {
watch(counter, () => console.log("childScope watch:", counter.value));
watch(doubled, () => console.log("childScope watch:", doubled.value));
watchEffect(() => console.log("childScope watchEffect:", doubled.value));
});

watch(counter, () => console.log("parentScope watch:", counter.value));
watch(doubled, () => console.log("parentScope watch:", doubled.value));
});

setTimeout(() => {
counter.value = 3; // 更改 counter 的值,会触发 parentScope、childScope、nestedScope 中的 watch 和 watchEffect
}, 1000);

setTimeout(() => {
parentScope.stop(); // 停止掉 parentScope 作用域内的所有 effect 副作用, 这时候包括 childScope 作用域内的副作用也会停止。因为 nestedScope 作用域与 parentScope 断开连接,所以不会受到影响
}, 3000);

setTimeout(() => {
counter.value = 5; // 再次更改 counter 的值,但是由于 parentScope 已经停止,所以不会触发 parentScope 和 childScope 中 watch 和 watchEffect 的副作用, 但是 nestedScope 作用域中的副作用会触发
}, 5000);

setTimeout(() => {
nestedScope.stop(); // 停止掉 nestedScope 作用域内的所有 effect 副作用
}, 7000);

setTimeout(() => {
counter.value = 5; // 再次更改 counter 的值,但是由于 nestedScope 已经停止,所以不会触发 nestedScope 中 watch 和 watchEffect 的副作用
}, 9000);

2.3 effectScope 停止回调

effectScope 停止监听时触发改函数 onScopeDispose,作用类似onUnmounted注意: onScopeDispose 必须放在 run 回调函数中才会触发。

onst counter = ref(2);
const scope = effectScope();
scope.run(() => {
const doubled = computed(() => counter.value * 2);
watch(doubled, () => console.log("watch:", doubled.value));
watchEffect(() => console.log("watchEffect:", doubled.value));

onScopeDispose(() => {
console.log("scope disposed");
});
});

setTimeout(() => {
counter.value = 3; // 更改 counter 的值,会触发 watch 和 watchEffect
}, 1000);

setTimeout(() => {
scope.stop(); // 停止掉 scope 作用域内的所有 effect 副作用
}, 3000);

setTimeout(() => {
counter.value = 5; // 再次更改 counter 的值,但是由于 scope 已经停止,所以不会触发 watch 和 watchEffect
}, 5000);