diff --git a/.changeset/early-ties-study.md b/.changeset/early-ties-study.md new file mode 100644 index 000000000000..bce26ebde4e8 --- /dev/null +++ b/.changeset/early-ties-study.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: improve old value implementation performance diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 9d2ad2baee4e..d1882e0e9709 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -1,4 +1,4 @@ -/** @import { Derived, Effect, Source, Value } from '#client' */ +/** @import { Derived, Effect, Source, Value, ValueNode } from '#client' */ import { DEV } from 'esm-env'; import { active_reaction, @@ -37,7 +37,70 @@ import { proxy } from '../proxy.js'; import { execute_derived } from './deriveds.js'; export let inspect_effects = new Set(); -export const old_values = new Map(); +/** @type { null | ValueNode } */ +export let old_value_node = null; +/** @type { null | ValueNode } */ +export let last_old_value_node = null; + +export function clear_old_value_node() { + old_value_node = null; + last_old_value_node = null; +} + +/** + * @param {Source} signal + */ +export function get_old_value_node(signal) { + let current = old_value_node; + while (current !== null) { + if (current.s === signal) { + return current; + } + current = current.n; + } + return null; +} + +/** + * @param {Source} signal + * @param {any} value + */ +function create_value_node(signal, value) { + return { + s: signal, + v: value, + n: null + }; +} + +/** + * @param {Source} signal + * @param {any} value + */ +function set_old_value(signal, value) { + const value_node = get_old_value_node(signal); + if (value_node !== null) { + value_node.v = value; + return; + } + add_old_value(signal, value); +} + +/** + * @param {Source} signal + * @param {any} value + */ +function add_old_value(signal, value) { + const value_node = create_value_node(signal, value); + + if (last_old_value_node === null) { + last_old_value_node = value_node; + old_value_node = value_node; + } else { + last_old_value_node.n = value_node; + last_old_value_node = value_node; + } +} /** * @param {Set} v @@ -155,9 +218,9 @@ export function internal_set(source, value) { var old_value = source.v; if (is_destroying_effect) { - old_values.set(source, value); + set_old_value(source, value); } else { - old_values.set(source, old_value); + add_old_value(source, old_value); } source.v = value; diff --git a/packages/svelte/src/internal/client/reactivity/types.d.ts b/packages/svelte/src/internal/client/reactivity/types.d.ts index 5ef0097649a4..ea6778c36743 100644 --- a/packages/svelte/src/internal/client/reactivity/types.d.ts +++ b/packages/svelte/src/internal/client/reactivity/types.d.ts @@ -74,3 +74,5 @@ export interface Effect extends Reaction { export type Source = Value; export type MaybeSource = T | Source; + +export type ValueNode = { s: Source; n: null | ValueNode; v: any }; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d790c0ad145a..3f5fd6b384d8 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -26,7 +26,7 @@ import { EFFECT_IS_UPDATING } from './constants.js'; import { flush_tasks } from './dom/task.js'; -import { internal_set, old_values } from './reactivity/sources.js'; +import { clear_old_value_node, internal_set, get_old_value_node } from './reactivity/sources.js'; import { destroy_derived_effects, update_derived } from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; @@ -676,7 +676,7 @@ function flush_queued_root_effects() { var collected_effects = process_effects(root_effects[i]); flush_queued_effects(collected_effects); } - old_values.clear(); + clear_old_value_node(); } } finally { is_flushing = false; @@ -931,8 +931,11 @@ export function get(signal) { } } - if (is_destroying_effect && old_values.has(signal)) { - return old_values.get(signal); + if (is_destroying_effect) { + const value_node = get_old_value_node(signal); + if (value_node !== null) { + return value_node.v; + } } return signal.v;