perf(reactivity): optimize effect/effectScope active state tracking
This commit is contained in:
parent
6b6889852f
commit
2993a24618
@ -45,7 +45,6 @@ export type DebuggerEventExtraInfo = {
|
|||||||
oldTarget?: Map<any, any> | Set<any>
|
oldTarget?: Map<any, any> | Set<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const effectStack: ReactiveEffect[] = []
|
|
||||||
let activeEffect: ReactiveEffect | undefined
|
let activeEffect: ReactiveEffect | undefined
|
||||||
|
|
||||||
export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
|
export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
|
||||||
@ -54,6 +53,7 @@ export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '')
|
|||||||
export class ReactiveEffect<T = any> {
|
export class ReactiveEffect<T = any> {
|
||||||
active = true
|
active = true
|
||||||
deps: Dep[] = []
|
deps: Dep[] = []
|
||||||
|
parent: ReactiveEffect | undefined = undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be attached after creation
|
* Can be attached after creation
|
||||||
@ -74,7 +74,7 @@ export class ReactiveEffect<T = any> {
|
|||||||
constructor(
|
constructor(
|
||||||
public fn: () => T,
|
public fn: () => T,
|
||||||
public scheduler: EffectScheduler | null = null,
|
public scheduler: EffectScheduler | null = null,
|
||||||
scope?: EffectScope | null
|
scope?: EffectScope
|
||||||
) {
|
) {
|
||||||
recordEffectScope(this, scope)
|
recordEffectScope(this, scope)
|
||||||
}
|
}
|
||||||
@ -83,31 +83,37 @@ export class ReactiveEffect<T = any> {
|
|||||||
if (!this.active) {
|
if (!this.active) {
|
||||||
return this.fn()
|
return this.fn()
|
||||||
}
|
}
|
||||||
if (!effectStack.length || !effectStack.includes(this)) {
|
let parent: ReactiveEffect | undefined = activeEffect
|
||||||
try {
|
let lastShouldTrack = shouldTrack
|
||||||
effectStack.push((activeEffect = this))
|
while (parent) {
|
||||||
enableTracking()
|
if (parent === this) {
|
||||||
|
return
|
||||||
trackOpBit = 1 << ++effectTrackDepth
|
|
||||||
|
|
||||||
if (effectTrackDepth <= maxMarkerBits) {
|
|
||||||
initDepMarkers(this)
|
|
||||||
} else {
|
|
||||||
cleanupEffect(this)
|
|
||||||
}
|
|
||||||
return this.fn()
|
|
||||||
} finally {
|
|
||||||
if (effectTrackDepth <= maxMarkerBits) {
|
|
||||||
finalizeDepMarkers(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
trackOpBit = 1 << --effectTrackDepth
|
|
||||||
|
|
||||||
resetTracking()
|
|
||||||
effectStack.pop()
|
|
||||||
const n = effectStack.length
|
|
||||||
activeEffect = n > 0 ? effectStack[n - 1] : undefined
|
|
||||||
}
|
}
|
||||||
|
parent = parent.parent
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.parent = activeEffect
|
||||||
|
activeEffect = this
|
||||||
|
shouldTrack = true
|
||||||
|
|
||||||
|
trackOpBit = 1 << ++effectTrackDepth
|
||||||
|
|
||||||
|
if (effectTrackDepth <= maxMarkerBits) {
|
||||||
|
initDepMarkers(this)
|
||||||
|
} else {
|
||||||
|
cleanupEffect(this)
|
||||||
|
}
|
||||||
|
return this.fn()
|
||||||
|
} finally {
|
||||||
|
if (effectTrackDepth <= maxMarkerBits) {
|
||||||
|
finalizeDepMarkers(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
trackOpBit = 1 << --effectTrackDepth
|
||||||
|
|
||||||
|
activeEffect = this.parent
|
||||||
|
shouldTrack = lastShouldTrack
|
||||||
|
this.parent = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +220,7 @@ export function track(target: object, type: TrackOpTypes, key: unknown) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isTracking() {
|
export function isTracking() {
|
||||||
return shouldTrack && activeEffect !== undefined
|
return shouldTrack && !!activeEffect
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trackEffects(
|
export function trackEffects(
|
||||||
|
@ -2,7 +2,6 @@ import { ReactiveEffect } from './effect'
|
|||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
|
||||||
let activeEffectScope: EffectScope | undefined
|
let activeEffectScope: EffectScope | undefined
|
||||||
const effectScopeStack: EffectScope[] = []
|
|
||||||
|
|
||||||
export class EffectScope {
|
export class EffectScope {
|
||||||
active = true
|
active = true
|
||||||
@ -42,24 +41,29 @@ export class EffectScope {
|
|||||||
|
|
||||||
on() {
|
on() {
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
effectScopeStack.push(this)
|
|
||||||
activeEffectScope = this
|
activeEffectScope = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
off() {
|
off() {
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
effectScopeStack.pop()
|
activeEffectScope = this.parent
|
||||||
activeEffectScope = effectScopeStack[effectScopeStack.length - 1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(fromParent?: boolean) {
|
stop(fromParent?: boolean) {
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
this.effects.forEach(e => e.stop())
|
let i, l
|
||||||
this.cleanups.forEach(cleanup => cleanup())
|
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) {
|
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
|
// nested scope, dereference from parent to avoid memory leaks
|
||||||
if (this.parent && !fromParent) {
|
if (this.parent && !fromParent) {
|
||||||
@ -81,9 +85,8 @@ export function effectScope(detached?: boolean) {
|
|||||||
|
|
||||||
export function recordEffectScope(
|
export function recordEffectScope(
|
||||||
effect: ReactiveEffect,
|
effect: ReactiveEffect,
|
||||||
scope?: EffectScope | null
|
scope: EffectScope | undefined = activeEffectScope
|
||||||
) {
|
) {
|
||||||
scope = scope || activeEffectScope
|
|
||||||
if (scope && scope.active) {
|
if (scope && scope.active) {
|
||||||
scope.effects.push(effect)
|
scope.effects.push(effect)
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
|
|||||||
|
|
||||||
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
|
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
|
||||||
export function isRef(r: any): r is Ref {
|
export function isRef(r: any): r is Ref {
|
||||||
return Boolean(r && r.__v_isRef === true)
|
return !!(r && r.__v_isRef === true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ref<T extends object>(
|
export function ref<T extends object>(
|
||||||
|
Loading…
Reference in New Issue
Block a user