fix(reactivity): should not recompute if computed does not track reactive data (#12341)
ci / test (push) Waiting to run Details
ci / continuous-release (push) Waiting to run Details
size data / upload (push) Waiting to run Details

close #12337
This commit is contained in:
edison 2025-05-01 22:02:17 +08:00 committed by GitHub
parent 8b848cbbd2
commit 0b23fd2383
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 6 deletions

View File

@ -1012,6 +1012,17 @@ describe('reactivity/computed', () => {
expect(cValue.value).toBe(1)
})
test('should not recompute if computed does not track reactive data', async () => {
const spy = vi.fn()
const c1 = computed(() => spy())
c1.value
ref(0).value++ // update globalVersion
c1.value
expect(spy).toBeCalledTimes(1)
})
test('computed should remain live after losing all subscribers', () => {
const state = reactive({ a: 1 })
const p = computed(() => state.a + 1)

View File

@ -49,6 +49,7 @@ export enum EffectFlags {
DIRTY = 1 << 4,
ALLOW_RECURSE = 1 << 5,
PAUSED = 1 << 6,
EVALUATED = 1 << 7,
}
/**
@ -377,22 +378,22 @@ export function refreshComputed(computed: ComputedRefImpl): undefined {
}
computed.globalVersion = globalVersion
const dep = computed.dep
computed.flags |= EffectFlags.RUNNING
// In SSR there will be no render effect, so the computed has no subscriber
// and therefore tracks no deps, thus we cannot rely on the dirty check.
// Instead, computed always re-evaluate and relies on the globalVersion
// fast path above for caching.
// #12337 if computed has no deps (does not rely on any reactive data) and evaluated,
// there is no need to re-evaluate.
if (
dep.version > 0 &&
!computed.isSSR &&
computed.deps &&
!isDirty(computed)
computed.flags & EffectFlags.EVALUATED &&
((!computed.deps && !(computed as any)._dirty) || !isDirty(computed))
) {
computed.flags &= ~EffectFlags.RUNNING
return
}
computed.flags |= EffectFlags.RUNNING
const dep = computed.dep
const prevSub = activeSub
const prevShouldTrack = shouldTrack
activeSub = computed
@ -402,6 +403,7 @@ export function refreshComputed(computed: ComputedRefImpl): undefined {
prepareDeps(computed)
const value = computed.fn(computed._value)
if (dep.version === 0 || hasChanged(value, computed._value)) {
computed.flags |= EffectFlags.EVALUATED
computed._value = value
dep.version++
}