oldTarget?: Map<any, any> | Set<any>
}
-const effectStack: ReactiveEffect[] = []
let activeEffect: ReactiveEffect | undefined
export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
export class ReactiveEffect<T = any> {
active = true
deps: Dep[] = []
+ parent: ReactiveEffect | undefined = undefined
/**
* Can be attached after creation
constructor(
public fn: () => T,
public scheduler: EffectScheduler | null = null,
- scope?: EffectScope | null
+ scope?: EffectScope
) {
recordEffectScope(this, scope)
}
if (!this.active) {
return this.fn()
}
- if (!effectStack.length || !effectStack.includes(this)) {
- try {
- effectStack.push((activeEffect = this))
- enableTracking()
+ let parent: ReactiveEffect | undefined = activeEffect
+ let lastShouldTrack = shouldTrack
+ while (parent) {
+ if (parent === this) {
+ return
+ }
+ parent = parent.parent
+ }
+ try {
+ this.parent = activeEffect
+ activeEffect = this
+ shouldTrack = true
- trackOpBit = 1 << ++effectTrackDepth
+ trackOpBit = 1 << ++effectTrackDepth
- if (effectTrackDepth <= maxMarkerBits) {
- initDepMarkers(this)
- } else {
- cleanupEffect(this)
- }
- return this.fn()
- } finally {
- if (effectTrackDepth <= maxMarkerBits) {
- finalizeDepMarkers(this)
- }
+ if (effectTrackDepth <= maxMarkerBits) {
+ initDepMarkers(this)
+ } else {
+ cleanupEffect(this)
+ }
+ return this.fn()
+ } finally {
+ if (effectTrackDepth <= maxMarkerBits) {
+ finalizeDepMarkers(this)
+ }
- trackOpBit = 1 << --effectTrackDepth
+ trackOpBit = 1 << --effectTrackDepth
- resetTracking()
- effectStack.pop()
- const n = effectStack.length
- activeEffect = n > 0 ? effectStack[n - 1] : undefined
- }
+ activeEffect = this.parent
+ shouldTrack = lastShouldTrack
+ this.parent = undefined
}
}
}
export function isTracking() {
- return shouldTrack && activeEffect !== undefined
+ return shouldTrack && !!activeEffect
}
export function trackEffects(
import { warn } from './warning'
let activeEffectScope: EffectScope | undefined
-const effectScopeStack: EffectScope[] = []
export class EffectScope {
active = true
on() {
if (this.active) {
- effectScopeStack.push(this)
activeEffectScope = this
}
}
off() {
if (this.active) {
- effectScopeStack.pop()
- activeEffectScope = effectScopeStack[effectScopeStack.length - 1]
+ activeEffectScope = this.parent
}
}
stop(fromParent?: boolean) {
if (this.active) {
- this.effects.forEach(e => e.stop())
- this.cleanups.forEach(cleanup => cleanup())
+ let i, l
+ for (i = 0, l = this.effects.length; i < l; i++) {
+ this.effects[i].stop()
+ }
+ for (i = 0, l = this.cleanups.length; i < l; i++) {
+ this.cleanups[i]()
+ }
if (this.scopes) {
- this.scopes.forEach(e => e.stop(true))
+ 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) {
export function recordEffectScope(
effect: ReactiveEffect,
- scope?: EffectScope | null
+ scope: EffectScope | undefined = activeEffectScope
) {
- scope = scope || activeEffectScope
if (scope && scope.active) {
scope.effects.push(effect)
}