Skip to content

Commit 361d33e

Browse files
trueadmRich-Harris
andauthored
chore: remove anchor node from each block items (#11836)
* fix: improve each block runtime heuristics be removing anchor text node * change message * conflict * merge * fix bug * Update .changeset/stale-nails-listen.md --------- Co-authored-by: Rich Harris <[email protected]>
1 parent be9b0a2 commit 361d33e

File tree

4 files changed

+69
-37
lines changed

4 files changed

+69
-37
lines changed

.changeset/stale-nails-listen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
chore: remove anchor node from each block items

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

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ function pause_effects(items, controlled_anchor, items_map) {
8585
var item = items[i];
8686
if (!is_controlled) {
8787
items_map.delete(item.k);
88-
item.o.remove();
8988
link(item.prev, item.next);
9089
}
9190
destroy_effect(item.e, !is_controlled);
@@ -182,11 +181,10 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
182181
break;
183182
}
184183

185-
var child_open = /** @type {Comment} */ (child_anchor);
186184
child_anchor = hydrate_anchor(child_anchor);
187185
var value = array[i];
188186
var key = get_key(value, i);
189-
item = create_item(child_open, child_anchor, prev, null, value, key, i, render_fn, flags);
187+
item = create_item(child_anchor, prev, null, value, key, i, render_fn, flags);
190188
state.items.set(key, item);
191189
child_anchor = /** @type {Comment} */ (child_anchor.nextSibling);
192190

@@ -293,22 +291,9 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
293291
item = items.get(key);
294292

295293
if (item === undefined) {
296-
var child_open = empty();
297-
var child_anchor = current ? current.o : anchor;
298-
299-
child_anchor.before(child_open);
300-
301-
prev = create_item(
302-
child_open,
303-
child_anchor,
304-
prev,
305-
prev.next,
306-
value,
307-
key,
308-
i,
309-
render_fn,
310-
flags
311-
);
294+
var child_anchor = current ? get_first_node(current.e) : anchor;
295+
296+
prev = create_item(child_anchor, prev, prev.next, value, key, i, render_fn, flags);
312297

313298
items.set(key, prev);
314299

@@ -451,7 +436,6 @@ function update_item(item, value, index, type) {
451436

452437
/**
453438
* @template V
454-
* @param {Comment | Text} open
455439
* @param {Node} anchor
456440
* @param {import('#client').EachItem | import('#client').EachState} prev
457441
* @param {import('#client').EachItem | null} next
@@ -462,7 +446,7 @@ function update_item(item, value, index, type) {
462446
* @param {number} flags
463447
* @returns {import('#client').EachItem}
464448
*/
465-
function create_item(open, anchor, prev, next, value, key, index, render_fn, flags) {
449+
function create_item(anchor, prev, next, value, key, index, render_fn, flags) {
466450
var previous_each_item = current_each_item;
467451

468452
try {
@@ -480,7 +464,6 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla
480464
a: null,
481465
// @ts-expect-error
482466
e: null,
483-
o: open,
484467
prev,
485468
next
486469
};
@@ -497,16 +480,52 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla
497480
}
498481
}
499482

483+
/**
484+
* @param {import('#client').TemplateNode} dom
485+
* @param {import("#client").Effect} effect
486+
* @returns {import('#client').TemplateNode}
487+
*/
488+
function get_adjusted_first_node(dom, effect) {
489+
if ((dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') || dom.nodeType === 8) {
490+
var adjusted = effect.first;
491+
var next;
492+
while (adjusted !== null) {
493+
next = adjusted.first;
494+
if (adjusted.dom !== null) {
495+
break;
496+
} else if (next === null) {
497+
return /** @type {import('#client').TemplateNode} */ (dom.previousSibling);
498+
}
499+
adjusted = next;
500+
}
501+
return get_first_node(/** @type {import("#client").Effect} */ (adjusted));
502+
}
503+
return dom;
504+
}
505+
506+
/**
507+
*
508+
* @param {import('#client').Effect} effect
509+
* @returns {import('#client').TemplateNode}
510+
*/
511+
function get_first_node(effect) {
512+
var dom = effect.dom;
513+
if (is_array(dom)) {
514+
return get_adjusted_first_node(dom[0], effect);
515+
}
516+
return get_adjusted_first_node(/** @type {import('#client').TemplateNode} **/ (dom), effect);
517+
}
518+
500519
/**
501520
* @param {import('#client').EachItem} item
502521
* @param {import('#client').EachItem | null} next
503522
* @param {Text | Element | Comment} anchor
504523
*/
505524
function move(item, next, anchor) {
506-
var end = item.next ? item.next.o : anchor;
507-
var dest = next ? next.o : anchor;
525+
var end = item.next ? get_first_node(item.next.e) : anchor;
526+
var dest = next ? get_first_node(next.e) : anchor;
508527

509-
var node = /** @type {import('#client').TemplateNode} */ (item.o);
528+
var node = get_first_node(item.e);
510529

511530
while (node !== end) {
512531
var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling);

packages/svelte/src/internal/client/dom/blocks/svelte-element.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ function swap_block_dom(effect, from, to) {
4747
* @returns {void}
4848
*/
4949
export function element(node, get_tag, is_svg, render_fn, get_namespace, location) {
50-
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
5150
const filename = DEV && location && current_component_context?.function.filename;
5251

5352
/** @type {string | null} */
@@ -64,6 +63,18 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
6463
/** @type {import('#client').Effect | null} */
6564
let effect;
6665

66+
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
67+
68+
// Remove the the hydrated effect dom entry for our dynamic element
69+
if (hydrating && is_array(parent_effect.dom)) {
70+
var remove_index = parent_effect.dom.indexOf(
71+
/** @type {import('#client').TemplateNode} */ (element)
72+
);
73+
if (remove_index !== -1) {
74+
parent_effect.dom.splice(remove_index, 1);
75+
}
76+
}
77+
6778
/**
6879
* The keyed `{#each ...}` item block, if any, that this element is inside.
6980
* We track this so we can set it when changing the element, allowing any
@@ -72,6 +83,7 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
7283
let each_item_block = current_each_item;
7384

7485
block(() => {
86+
const element_effect = /** @type {import('#client').Effect} */ (current_effect);
7587
const next_tag = get_tag() || null;
7688
const ns = get_namespace
7789
? get_namespace()
@@ -124,6 +136,13 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
124136
};
125137
}
126138

139+
if (prev_element && !hydrating) {
140+
swap_block_dom(element_effect, prev_element, element);
141+
prev_element.remove();
142+
} else {
143+
push_template_node(element, element_effect);
144+
}
145+
127146
if (render_fn) {
128147
// If hydrating, use the existing ssr comment as the anchor so that the
129148
// inner open and close methods can pick up the existing nodes correctly
@@ -144,15 +163,6 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
144163

145164
anchor.before(element);
146165

147-
if (!hydrating) {
148-
if (prev_element) {
149-
swap_block_dom(parent_effect, prev_element, element);
150-
prev_element.remove();
151-
} else {
152-
push_template_node(element, parent_effect);
153-
}
154-
}
155-
156166
// See below
157167
return noop;
158168
});

packages/svelte/src/internal/client/types.d.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ export type EachItem = {
8585
i: number | Source<number>;
8686
/** key */
8787
k: unknown;
88-
/** anchor for items inserted before this */
89-
o: Comment | Text;
9088
prev: EachItem | EachState;
9189
next: EachItem | null;
9290
};

0 commit comments

Comments
 (0)