Skip to content

Commit 6c356a8

Browse files
committed
fix(reactivity): prevent unnecessary computed recalculations in dev mode
Fix computed properties being recalculated unnecessarily in development mode when unrelated reactive values change, while production mode works correctly. This issue was caused by version lag in dependency tracking during development builds. The fix introduces a conservative dependency check that: - Only considers dependencies truly dirty when version diff > 1 - Syncs link versions to prevent accumulating lag - Maintains consistency between dev and production behavior - Preserves all debugging functionality Closes issue with computed performance regression in development mode.
1 parent 75220c7 commit 6c356a8

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

packages/reactivity/src/effect.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ function cleanupDeps(sub: Subscriber) {
327327
// The new head is the last node seen which wasn't removed
328328
// from the doubly-linked list
329329
head = link
330+
// Sync link version to maintain consistency for future dirty checks
331+
link.version = link.dep.version
330332
}
331333

332334
// restore previous active link if any
@@ -378,6 +380,36 @@ export function refreshComputed(computed: ComputedRefImpl): undefined {
378380
}
379381
computed.globalVersion = globalVersion
380382

383+
// Enhanced dependency check for development mode to ensure consistent behavior
384+
// with production mode by avoiding unnecessary recomputations due to version lag
385+
if (__DEV__ && computed.flags & EffectFlags.EVALUATED && computed.deps) {
386+
let actuallyDirty = false
387+
let link: Link | undefined = computed.deps
388+
while (link) {
389+
// Refresh nested computed dependencies first
390+
if (link.dep.computed && link.dep.computed !== computed) {
391+
refreshComputed(link.dep.computed)
392+
}
393+
394+
// Conservative dirty check: only consider dirty if version difference > 1
395+
// This accounts for the cleanup lag and prevents false positives
396+
const versionDiff = link.dep.version - link.version
397+
if (versionDiff > 1) {
398+
actuallyDirty = true
399+
break
400+
} else if (versionDiff === 1) {
401+
// Sync version to prevent accumulating lag
402+
link.version = link.dep.version
403+
}
404+
405+
link = link.nextDep
406+
}
407+
408+
if (!actuallyDirty) {
409+
return
410+
}
411+
}
412+
381413
// In SSR there will be no render effect, so the computed has no subscriber
382414
// and therefore tracks no deps, thus we cannot rely on the dirty check.
383415
// Instead, computed always re-evaluate and relies on the globalVersion

0 commit comments

Comments
 (0)