Skip to content

Commit f7c8fd5

Browse files
authored
Revert "chore: apply each block controlled teardown optimization (#11045)" (#11049)
This reverts commit 1afec80.
1 parent bb1d229 commit f7c8fd5

File tree

2 files changed

+38
-54
lines changed

2 files changed

+38
-54
lines changed

packages/svelte/src/internal/client/dom/blocks/each.js

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@ import { untrack } from '../../runtime.js';
1515
import {
1616
block,
1717
branch,
18-
destroy_effect,
1918
effect,
20-
run_out_transitions,
21-
pause_children,
2219
pause_effect,
20+
pause_effects,
2321
resume_effect
2422
} from '../../reactivity/effects.js';
2523
import { source, mutable_source, set } from '../../reactivity/sources.js';
@@ -41,39 +39,6 @@ export function set_current_each_item(item) {
4139
current_each_item = item;
4240
}
4341

44-
/**
45-
* Pause multiple effects simultaneously, and coordinate their
46-
* subsequent destruction. Used in each blocks
47-
* @param {import('#client').Effect[]} effects
48-
* @param {null | Node} controlled_anchor
49-
* @param {() => void} [callback]
50-
*/
51-
function pause_effects(effects, controlled_anchor, callback) {
52-
/** @type {import('#client').TransitionManager[]} */
53-
var transitions = [];
54-
var length = effects.length;
55-
56-
for (var i = 0; i < length; i++) {
57-
pause_children(effects[i], transitions, true);
58-
}
59-
60-
// If we have a controlled anchor, it means that the each block is inside a single
61-
// DOM element, so we can apply a fast-path for clearing the contents of the element.
62-
if (effects.length > 0 && transitions.length === 0 && controlled_anchor !== null) {
63-
var parent_node = /** @type {Element} */ (controlled_anchor.parentNode);
64-
parent_node.textContent = '';
65-
parent_node.append(controlled_anchor);
66-
}
67-
68-
run_out_transitions(transitions, () => {
69-
for (var i = 0; i < length; i++) {
70-
destroy_effect(effects[i]);
71-
}
72-
73-
if (callback !== undefined) callback();
74-
});
75-
}
76-
7742
/**
7843
* @template V
7944
* @param {Element | Comment} anchor The next sibling node, or the parent node if this is a 'controlled' block
@@ -180,6 +145,7 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
180145
}
181146

182147
if (!hydrating) {
148+
// TODO add 'empty controlled block' optimisation here
183149
reconcile_fn(array, state, anchor, render_fn, flags, keys);
184150
}
185151

@@ -278,9 +244,7 @@ function reconcile_indexed_array(array, state, anchor, render_fn, flags) {
278244
effects.push(a_items[i].e);
279245
}
280246

281-
var controlled_anchor = (flags & EACH_IS_CONTROLLED) !== 0 && b === 0 ? anchor : null;
282-
283-
pause_effects(effects, controlled_anchor, () => {
247+
pause_effects(effects, () => {
284248
state.items.length = b;
285249
});
286250
}
@@ -310,7 +274,6 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
310274

311275
var is_animated = (flags & EACH_IS_ANIMATED) !== 0;
312276
var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
313-
var is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
314277
var start = 0;
315278
var item;
316279

@@ -418,11 +381,6 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
418381
// I fully understand this part)
419382
if (moved) {
420383
mark_lis(sources);
421-
} else if (is_controlled && to_destroy.length === a_items.length) {
422-
// We can optimize the case in which all items are replaced —
423-
// destroy everything first, then append new items
424-
pause_effects(to_destroy, anchor);
425-
to_destroy = [];
426384
}
427385

428386
// working from the back, insert new or moved items
@@ -463,9 +421,9 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
463421
});
464422
}
465423

466-
var controlled_anchor = is_controlled && b === 0 ? anchor : null;
467-
468-
pause_effects(to_destroy, controlled_anchor, () => {
424+
// TODO: would be good to avoid this closure in the case where we have no
425+
// transitions at all. It would make it far more JIT friendly in the hot cases.
426+
pause_effects(to_destroy, () => {
469427
state.items = b_items;
470428
});
471429
}

packages/svelte/src/internal/client/reactivity/effects.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
IS_ELSEIF
2828
} from '../constants.js';
2929
import { set } from './sources.js';
30+
import { noop } from '../../shared/utils.js';
3031
import { remove } from '../dom/reconciler.js';
3132

3233
/**
@@ -294,25 +295,50 @@ export function destroy_effect(effect) {
294295
* completed, and if the state change is reversed then we _resume_ it.
295296
* A paused effect does not update, and the DOM subtree becomes inert.
296297
* @param {import('#client').Effect} effect
297-
* @param {() => void} [callback]
298+
* @param {() => void} callback
298299
*/
299-
export function pause_effect(effect, callback) {
300+
export function pause_effect(effect, callback = noop) {
300301
/** @type {import('#client').TransitionManager[]} */
301302
var transitions = [];
302303

303304
pause_children(effect, transitions, true);
304305

305-
run_out_transitions(transitions, () => {
306+
out(transitions, () => {
306307
destroy_effect(effect);
307-
if (callback) callback();
308+
callback();
309+
});
310+
}
311+
312+
/**
313+
* Pause multiple effects simultaneously, and coordinate their
314+
* subsequent destruction. Used in each blocks
315+
* @param {import('#client').Effect[]} effects
316+
* @param {() => void} callback
317+
*/
318+
export function pause_effects(effects, callback = noop) {
319+
/** @type {import('#client').TransitionManager[]} */
320+
var transitions = [];
321+
var length = effects.length;
322+
323+
for (var i = 0; i < length; i++) {
324+
pause_children(effects[i], transitions, true);
325+
}
326+
327+
// TODO: would be good to avoid this closure in the case where we have no
328+
// transitions at all. It would make it far more JIT friendly in the hot cases.
329+
out(transitions, () => {
330+
for (var i = 0; i < length; i++) {
331+
destroy_effect(effects[i]);
332+
}
333+
callback();
308334
});
309335
}
310336

311337
/**
312338
* @param {import('#client').TransitionManager[]} transitions
313339
* @param {() => void} fn
314340
*/
315-
export function run_out_transitions(transitions, fn) {
341+
function out(transitions, fn) {
316342
var remaining = transitions.length;
317343
if (remaining > 0) {
318344
var check = () => --remaining || fn();
@@ -329,7 +355,7 @@ export function run_out_transitions(transitions, fn) {
329355
* @param {import('#client').TransitionManager[]} transitions
330356
* @param {boolean} local
331357
*/
332-
export function pause_children(effect, transitions, local) {
358+
function pause_children(effect, transitions, local) {
333359
if ((effect.f & INERT) !== 0) return;
334360
effect.f ^= INERT;
335361

0 commit comments

Comments
 (0)