diff --git a/.changeset/gentle-ties-fetch.md b/.changeset/gentle-ties-fetch.md new file mode 100644 index 000000000000..830278581134 --- /dev/null +++ b/.changeset/gentle-ties-fetch.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: improve controlled each block cleanup performance diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 8c59e3aebab3..bef42ea5c4ff 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -68,17 +68,20 @@ function pause_effects(items, controlled_anchor, callback) { pause_children(items[i].e, transitions, true); } + var is_controlled = length > 0 && transitions.length === 0 && controlled_anchor !== null; // If we have a controlled anchor, it means that the each block is inside a single // DOM element, so we can apply a fast-path for clearing the contents of the element. - if (length > 0 && transitions.length === 0 && controlled_anchor !== null) { - var parent_node = /** @type {Element} */ (controlled_anchor.parentNode); + if (is_controlled) { + var parent_node = /** @type {Element} */ ( + /** @type {Element} */ (controlled_anchor).parentNode + ); clear_text_content(parent_node); - parent_node.append(controlled_anchor); + parent_node.append(/** @type {Element} */ (controlled_anchor)); } run_out_transitions(transitions, () => { for (var i = 0; i < length; i++) { - destroy_effect(items[i].e); + destroy_effect(items[i].e, !is_controlled); } if (callback !== undefined) callback(); diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 6411e8109af7..f7db38f3d148 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -311,16 +311,17 @@ export function execute_effect_teardown(effect) { /** * @param {import('#client').Effect} effect + * @param {boolean} [remove_dom] * @returns {void} */ -export function destroy_effect(effect) { +export function destroy_effect(effect, remove_dom = true) { var dom = effect.dom; - if (dom !== null) { + if (dom !== null && remove_dom) { remove(dom); } - destroy_effect_children(effect); + destroy_effect_children(effect, remove_dom); remove_reactions(effect, 0); set_signal_status(effect, DESTROYED); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 1f0a0bfdabac..8fe016532da0 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -478,16 +478,17 @@ export function remove_reactions(signal, start_index) { /** * @param {import('#client').Reaction} signal + * @param {boolean} [remove_dom] * @returns {void} */ -export function destroy_effect_children(signal) { +export function destroy_effect_children(signal, remove_dom = true) { let effect = signal.first; signal.first = null; signal.last = null; var sibling; while (effect !== null) { sibling = effect.next; - destroy_effect(effect); + destroy_effect(effect, remove_dom); effect = sibling; } }