Skip to content

Commit ec917cf

Browse files
perf(reactivity): avoid unnecessary recursion in removeSub (#12135)
1 parent f6d9926 commit ec917cf

File tree

2 files changed

+45
-12
lines changed

2 files changed

+45
-12
lines changed

packages/reactivity/__tests__/computed.spec.ts

+32
Original file line numberDiff line numberDiff line change
@@ -1107,4 +1107,36 @@ describe('reactivity/computed', () => {
11071107
end.prop4.value,
11081108
]).toMatchObject([-2, -4, 2, 3])
11091109
})
1110+
1111+
test('performance when removing dependencies from deeply nested computeds', () => {
1112+
const base = ref(1)
1113+
const trigger = ref(true)
1114+
const computeds: ComputedRef<number>[] = []
1115+
1116+
const LAYERS = 30
1117+
1118+
for (let i = 0; i < LAYERS; i++) {
1119+
const earlier = [...computeds]
1120+
1121+
computeds.push(
1122+
computed(() => {
1123+
return base.value + earlier.reduce((sum, c) => sum + c.value, 0)
1124+
}),
1125+
)
1126+
}
1127+
1128+
const tail = computed(() =>
1129+
trigger.value ? computeds[computeds.length - 1].value : 0,
1130+
)
1131+
1132+
const t0 = performance.now()
1133+
expect(tail.value).toBe(2 ** (LAYERS - 1))
1134+
const t1 = performance.now()
1135+
expect(t1 - t0).toBeLessThan(process.env.CI ? 100 : 30)
1136+
1137+
trigger.value = false
1138+
expect(tail.value).toBe(0)
1139+
const t2 = performance.now()
1140+
expect(t2 - t1).toBeLessThan(process.env.CI ? 100 : 30)
1141+
})
11101142
})

packages/reactivity/src/effect.ts

+13-12
Original file line numberDiff line numberDiff line change
@@ -426,23 +426,24 @@ function removeSub(link: Link, soft = false) {
426426
nextSub.prevSub = prevSub
427427
link.nextSub = undefined
428428
}
429-
if (dep.subs === link) {
430-
// was previous tail, point new tail to prev
431-
dep.subs = prevSub
432-
}
433429
if (__DEV__ && dep.subsHead === link) {
434430
// was previous head, point new head to next
435431
dep.subsHead = nextSub
436432
}
437433

438-
if (!dep.subs && dep.computed) {
439-
// if computed, unsubscribe it from all its deps so this computed and its
440-
// value can be GCed
441-
dep.computed.flags &= ~EffectFlags.TRACKING
442-
for (let l = dep.computed.deps; l; l = l.nextDep) {
443-
// here we are only "soft" unsubscribing because the computed still keeps
444-
// referencing the deps and the dep should not decrease its sub count
445-
removeSub(l, true)
434+
if (dep.subs === link) {
435+
// was previous tail, point new tail to prev
436+
dep.subs = prevSub
437+
438+
if (!prevSub && dep.computed) {
439+
// if computed, unsubscribe it from all its deps so this computed and its
440+
// value can be GCed
441+
dep.computed.flags &= ~EffectFlags.TRACKING
442+
for (let l = dep.computed.deps; l; l = l.nextDep) {
443+
// here we are only "soft" unsubscribing because the computed still keeps
444+
// referencing the deps and the dep should not decrease its sub count
445+
removeSub(l, true)
446+
}
446447
}
447448
}
448449

0 commit comments

Comments
 (0)