let activeEffectScope;
// effectScope可以对内部的响应式对象的副作用effect进行统一管理。
class EffectScope {
//effectScope接收一个boolean值,如果传true代表游离模式,那么创建的scope不会被父scope收集,通俗来讲,如果是游离模式,那么scope之间是不存在父子关系的,每一个scope都是独立的。
constructor(detached = false) {
/**
* @internal //内部的
*/
this.active = true;
/**
* @internal
*/
this.effects = [];
/**
* @internal
*/
this.cleanups = [];
if (!detached && activeEffectScope) {
this.parent = activeEffectScope;
this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;
}
}
//如果detached为false,并且存在activeEffectScope(activeEffectScope是个全局变量)的情况,会将activeEffectScope赋值给this.parent,
//同时会将当前EffectScope实例放入activeEffectScope.scopes中,并将activeEffectScope.scopes最后一个索引赋值给当前EffectScope实例的index属性。
//这样就可以通过this.index来获取EffectScope实例在父scope中的索引位置。
//run方法会首先对this.active进行判断,如果this.active为true,也就是EffectScope处于激活状态,那么会将this赋给activeEffectScope,然后执行fn,并返回其执行结果。
//当fn执行完毕后,将activeEffectScope改为this.parent。
run(fn) {
if (this.active) {
const currentEffectScope = activeEffectScope;
try {
activeEffectScope = this;
return fn();
}
finally {
activeEffectScope = currentEffectScope;
}
}
else {
warn(`cannot run an inactive effect scope.`);
}
}
/**
* This should only be called on non-detached scopes
* 不分离 -- non-detached
* @internal
*/
// on方法会将activeEffectScope指向当前EffectScope实例。
on() {
activeEffectScope = this;
}
/**
* This should only be called on non-detached scopes
* @internal
*/
//off方法会将activeEffectScope指向当前EffectScope实例的父scope。
off() {
activeEffectScope = this.parent;
}
//stop函数的作用是清除scope内的所有的响应式效果,包括子scope。
//stop接收一个boolean类型的fromParent参数,如果fromParent为true,stop将不会删除在父scope中的引用。
stop(fromParent) {
if (this.active) {
let i, l;
// 调用ReactiveEffect.prototype.stop,清除scope内所有响应式效果
for (i = 0, l = this.effects.length; i < l; i++) {
this.effects[i].stop();
}
// 触发scope销毁时的监听函数
for (i = 0, l = this.cleanups.length; i < l; i++) {
this.cleanups[i]();
}
// 销毁子scope
if (this.scopes) {
for (i = 0, l = this.scopes.length; i < l; i++) {
this.scopes[i].stop(true);
}
}
// 嵌套范围,从父级取消引用以避免内存泄漏
// nested scope, dereference from parent to avoid memory leaks
if (this.parent && !fromParent) {
// optimized O(1) removal 优化的O(1)去除
// 获取父scope的中最后一个scope
const last = this.parent.scopes.pop();
if (last && last !== this) {
// 将last放在当前scope在parent.scopes中的索引位置
this.parent.scopes[this.index] = last;
// last.index改为this.index
last.index = this.index;
}
}
// 修改scope的激活状态
this.active = false;
}
}
}
stop中的所有操作都要建立在scope处于激活状态的基础上。首先遍历this.effects执行元素的stop方法。
for (i = 0, l = this.effects.length; i < l; i++) {
this.effects[i].stop()
}
scope.effects存储的是在run过程中获取到的ReactiveEffect实例,这些ReactiveEffect实例会通过一个recordEffectScope方法被添加到scope.effects中。
export function recordEffectScope(
effect: ReactiveEffect,
scope: EffectScope | undefined = activeEffectScope
) {
if (scope && scope.active) {
scope.effects.push(effect)
}
}
当遍历完scope.effects或,会遍历scope.cleanups属性。
for (i = 0, l = this.cleanups.length; i < l; i++) {
this.cleanups[i]()
}
scope.cleanups中保存的是通过onScopeDispose添加的scope销毁监听函数。
export function onScopeDispose(fn: () => void) {
if (activeEffectScope) {
activeEffectScope.cleanups.push(fn)
} else if (__DEV__) {
warn(
`onScopeDispose() is called when there is no active effect scope` +
` to be associated with.`
)
}
}
如果当前scope存在scopes属性,意味着当前scope存在子scope,所以需要将所有子scope也进行销毁。
if (this.scopes) {
for (i = 0, l = this.scopes.length; i < l; i++) {
this.scopes[i].stop(true)
}
}
如果当前scope存在parent的话,需要将scope从其parent中移除。
if (this.parent && !fromParent) {
// 获取父scope的中最后一个scope
const last = this.parent.scopes!.pop()
// last不是当前的scope
if (last && last !== this) {
// 将last放在当前scope在parent.scopes中的索引位置
this.parent.scopes![this.index!] = last
// last.index改为this.index
last.index = this.index!
}
}
这里的移除过逻辑是,先获取当前scope的父scope中的所有子scope,然后取出最后一个scope,这里用last代表(注意last不一定和当前scope相同),如果last和当前scope不同的话,需要让last替换当前scope,这样我们就把当前scope从其父scope中移除了。这里仅仅替换是不够的,因为last.index此时还是之前父scope的最后一个索引,所以还需要把last.index改为当前scope在其父scope.scopes中的位置。这样就完全移除了scope。
最后,需要把scope的激活状态改为false。
this.active = false