diff --git a/.changeset/ninety-kings-attend.md b/.changeset/ninety-kings-attend.md new file mode 100644 index 0000000000..40913dab67 --- /dev/null +++ b/.changeset/ninety-kings-attend.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: avoid re-traversing the effect tree after `$:` assignments diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 73e4a30fa4..1bd067cf16 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -23,6 +23,7 @@ import { async_mode_flag } from '../../flags/index.js'; import { deferred, define_property, includes } from '../../shared/utils.js'; import { active_effect, + active_reaction, get, increment_write_version, is_dirty, @@ -860,7 +861,7 @@ export function schedule_effect(signal) { // in sync mode, render effects run during traversal. in an extreme edge case // they can be made dirty after they have already been visited, in which // case we shouldn't bail out - if (async_mode_flag || (signal.f & RENDER_EFFECT) === 0) { + if (async_mode_flag || active_reaction === null || (active_reaction.f & DERIVED) === 0) { return; } } diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index b3d37659ea..3118851277 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -10,7 +10,8 @@ import { set_active_reaction, set_is_destroying_effect, untrack, - untracking + untracking, + set_active_effect } from '../runtime.js'; import { DIRTY, @@ -316,7 +317,19 @@ export function legacy_pre_effect(deps, fn) { if (token.ran) return; token.ran = true; - untrack(fn); + + var effect = /** @type {Effect} */ (active_effect); + + // here, we lie: by setting `active_effect` to be the parent branch, any writes + // that happen inside `fn` will _not_ cause an unnecessary reschedule, because + // the affected effects will be children of `active_effect`. this is safe + // because these effects are known to run in the correct order + try { + set_active_effect(effect.parent); + untrack(fn); + } finally { + set_active_effect(effect); + } }); }