mirror of https://github.com/vuejs/core.git
fix(reactivity): avoid infinite recursion from side effects in computed getter (#10232)
close #10214
This commit is contained in:
parent
6c7e0bd88f
commit
0bced13ee5
|
@ -482,8 +482,12 @@ describe('reactivity/computed', () => {
|
||||||
c3.value
|
c3.value
|
||||||
|
|
||||||
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
||||||
expect(c2.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
expect(c2.effect._dirtyLevel).toBe(
|
||||||
expect(c3.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||||
|
)
|
||||||
|
expect(c3.effect._dirtyLevel).toBe(
|
||||||
|
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work when chained(ref+computed)', () => {
|
it('should work when chained(ref+computed)', () => {
|
||||||
|
@ -550,8 +554,12 @@ describe('reactivity/computed', () => {
|
||||||
|
|
||||||
c3.value
|
c3.value
|
||||||
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
||||||
expect(c2.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
expect(c2.effect._dirtyLevel).toBe(
|
||||||
expect(c3.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||||
|
)
|
||||||
|
expect(c3.effect._dirtyLevel).toBe(
|
||||||
|
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||||
|
)
|
||||||
|
|
||||||
v1.value.v.value = 999
|
v1.value.v.value = 999
|
||||||
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
||||||
|
@ -581,4 +589,26 @@ describe('reactivity/computed', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(serializeInner(root)).toBe(`2`)
|
expect(serializeInner(root)).toBe(`2`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not trigger effect scheduler by recurse computed effect', async () => {
|
||||||
|
const v = ref('Hello')
|
||||||
|
const c = computed(() => {
|
||||||
|
v.value += ' World'
|
||||||
|
return v.value
|
||||||
|
})
|
||||||
|
const Comp = {
|
||||||
|
setup: () => {
|
||||||
|
return () => c.value
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
|
||||||
|
render(h(Comp), root)
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe('Hello World')
|
||||||
|
|
||||||
|
v.value += ' World'
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe('Hello World World World World')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -43,7 +43,13 @@ export class ComputedRefImpl<T> {
|
||||||
) {
|
) {
|
||||||
this.effect = new ReactiveEffect(
|
this.effect = new ReactiveEffect(
|
||||||
() => getter(this._value),
|
() => getter(this._value),
|
||||||
() => triggerRefValue(this, DirtyLevels.MaybeDirty),
|
() =>
|
||||||
|
triggerRefValue(
|
||||||
|
this,
|
||||||
|
this.effect._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect
|
||||||
|
? DirtyLevels.MaybeDirty_ComputedSideEffect
|
||||||
|
: DirtyLevels.MaybeDirty,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
this.effect.computed = this
|
this.effect.computed = this
|
||||||
this.effect.active = this._cacheable = !isSSR
|
this.effect.active = this._cacheable = !isSSR
|
||||||
|
@ -60,8 +66,8 @@ export class ComputedRefImpl<T> {
|
||||||
triggerRefValue(self, DirtyLevels.Dirty)
|
triggerRefValue(self, DirtyLevels.Dirty)
|
||||||
}
|
}
|
||||||
trackRefValue(self)
|
trackRefValue(self)
|
||||||
if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty) {
|
if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty_ComputedSideEffect) {
|
||||||
triggerRefValue(self, DirtyLevels.MaybeDirty)
|
triggerRefValue(self, DirtyLevels.MaybeDirty_ComputedSideEffect)
|
||||||
}
|
}
|
||||||
return self._value
|
return self._value
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ export enum ReactiveFlags {
|
||||||
export enum DirtyLevels {
|
export enum DirtyLevels {
|
||||||
NotDirty = 0,
|
NotDirty = 0,
|
||||||
QueryingDirty = 1,
|
QueryingDirty = 1,
|
||||||
MaybeDirty = 2,
|
MaybeDirty_ComputedSideEffect = 2,
|
||||||
Dirty = 3,
|
MaybeDirty = 3,
|
||||||
|
Dirty = 4,
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,10 @@ export class ReactiveEffect<T = any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public get dirty() {
|
public get dirty() {
|
||||||
if (this._dirtyLevel === DirtyLevels.MaybeDirty) {
|
if (
|
||||||
|
this._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect ||
|
||||||
|
this._dirtyLevel === DirtyLevels.MaybeDirty
|
||||||
|
) {
|
||||||
this._dirtyLevel = DirtyLevels.QueryingDirty
|
this._dirtyLevel = DirtyLevels.QueryingDirty
|
||||||
pauseTracking()
|
pauseTracking()
|
||||||
for (let i = 0; i < this._depsLength; i++) {
|
for (let i = 0; i < this._depsLength; i++) {
|
||||||
|
@ -309,7 +312,10 @@ export function triggerEffects(
|
||||||
effect.onTrigger?.(extend({ effect }, debuggerEventExtraInfo))
|
effect.onTrigger?.(extend({ effect }, debuggerEventExtraInfo))
|
||||||
}
|
}
|
||||||
effect.trigger()
|
effect.trigger()
|
||||||
if (!effect._runnings || effect.allowRecurse) {
|
if (
|
||||||
|
(!effect._runnings || effect.allowRecurse) &&
|
||||||
|
effect._dirtyLevel !== DirtyLevels.MaybeDirty_ComputedSideEffect
|
||||||
|
) {
|
||||||
effect._shouldSchedule = false
|
effect._shouldSchedule = false
|
||||||
if (effect.scheduler) {
|
if (effect.scheduler) {
|
||||||
queueEffectSchedulers.push(effect.scheduler)
|
queueEffectSchedulers.push(effect.scheduler)
|
||||||
|
|
|
@ -49,11 +49,10 @@ export function trackRefValue(ref: RefBase<any>) {
|
||||||
ref = toRaw(ref)
|
ref = toRaw(ref)
|
||||||
trackEffect(
|
trackEffect(
|
||||||
activeEffect,
|
activeEffect,
|
||||||
ref.dep ||
|
(ref.dep ??= createDep(
|
||||||
(ref.dep = createDep(
|
() => (ref.dep = undefined),
|
||||||
() => (ref.dep = undefined),
|
ref instanceof ComputedRefImpl ? ref : undefined,
|
||||||
ref instanceof ComputedRefImpl ? ref : undefined,
|
)),
|
||||||
)),
|
|
||||||
__DEV__
|
__DEV__
|
||||||
? {
|
? {
|
||||||
target: ref,
|
target: ref,
|
||||||
|
|
Loading…
Reference in New Issue