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
|
||||
|
||||
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
||||
expect(c2.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
||||
expect(c3.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
||||
expect(c2.effect._dirtyLevel).toBe(
|
||||
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||
)
|
||||
expect(c3.effect._dirtyLevel).toBe(
|
||||
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||
)
|
||||
})
|
||||
|
||||
it('should work when chained(ref+computed)', () => {
|
||||
|
@ -550,8 +554,12 @@ describe('reactivity/computed', () => {
|
|||
|
||||
c3.value
|
||||
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
||||
expect(c2.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
||||
expect(c3.effect._dirtyLevel).toBe(DirtyLevels.MaybeDirty)
|
||||
expect(c2.effect._dirtyLevel).toBe(
|
||||
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||
)
|
||||
expect(c3.effect._dirtyLevel).toBe(
|
||||
DirtyLevels.MaybeDirty_ComputedSideEffect,
|
||||
)
|
||||
|
||||
v1.value.v.value = 999
|
||||
expect(c1.effect._dirtyLevel).toBe(DirtyLevels.Dirty)
|
||||
|
@ -581,4 +589,26 @@ describe('reactivity/computed', () => {
|
|||
await nextTick()
|
||||
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(
|
||||
() => 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.active = this._cacheable = !isSSR
|
||||
|
@ -60,8 +66,8 @@ export class ComputedRefImpl<T> {
|
|||
triggerRefValue(self, DirtyLevels.Dirty)
|
||||
}
|
||||
trackRefValue(self)
|
||||
if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty) {
|
||||
triggerRefValue(self, DirtyLevels.MaybeDirty)
|
||||
if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty_ComputedSideEffect) {
|
||||
triggerRefValue(self, DirtyLevels.MaybeDirty_ComputedSideEffect)
|
||||
}
|
||||
return self._value
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ export enum ReactiveFlags {
|
|||
export enum DirtyLevels {
|
||||
NotDirty = 0,
|
||||
QueryingDirty = 1,
|
||||
MaybeDirty = 2,
|
||||
Dirty = 3,
|
||||
MaybeDirty_ComputedSideEffect = 2,
|
||||
MaybeDirty = 3,
|
||||
Dirty = 4,
|
||||
}
|
||||
|
|
|
@ -76,7 +76,10 @@ export class ReactiveEffect<T = any> {
|
|||
}
|
||||
|
||||
public get dirty() {
|
||||
if (this._dirtyLevel === DirtyLevels.MaybeDirty) {
|
||||
if (
|
||||
this._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect ||
|
||||
this._dirtyLevel === DirtyLevels.MaybeDirty
|
||||
) {
|
||||
this._dirtyLevel = DirtyLevels.QueryingDirty
|
||||
pauseTracking()
|
||||
for (let i = 0; i < this._depsLength; i++) {
|
||||
|
@ -309,7 +312,10 @@ export function triggerEffects(
|
|||
effect.onTrigger?.(extend({ effect }, debuggerEventExtraInfo))
|
||||
}
|
||||
effect.trigger()
|
||||
if (!effect._runnings || effect.allowRecurse) {
|
||||
if (
|
||||
(!effect._runnings || effect.allowRecurse) &&
|
||||
effect._dirtyLevel !== DirtyLevels.MaybeDirty_ComputedSideEffect
|
||||
) {
|
||||
effect._shouldSchedule = false
|
||||
if (effect.scheduler) {
|
||||
queueEffectSchedulers.push(effect.scheduler)
|
||||
|
|
|
@ -49,11 +49,10 @@ export function trackRefValue(ref: RefBase<any>) {
|
|||
ref = toRaw(ref)
|
||||
trackEffect(
|
||||
activeEffect,
|
||||
ref.dep ||
|
||||
(ref.dep = createDep(
|
||||
() => (ref.dep = undefined),
|
||||
ref instanceof ComputedRefImpl ? ref : undefined,
|
||||
)),
|
||||
(ref.dep ??= createDep(
|
||||
() => (ref.dep = undefined),
|
||||
ref instanceof ComputedRefImpl ? ref : undefined,
|
||||
)),
|
||||
__DEV__
|
||||
? {
|
||||
target: ref,
|
||||
|
|
Loading…
Reference in New Issue