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 <rich.harris@vercel.com>
pull/12116/head
Dominic Gannaway 6 months ago committed by GitHub
parent be9b0a275a
commit 361d33e60f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
chore: remove anchor node from each block items

@ -85,7 +85,6 @@ function pause_effects(items, controlled_anchor, items_map) {
var item = items[i]; var item = items[i];
if (!is_controlled) { if (!is_controlled) {
items_map.delete(item.k); items_map.delete(item.k);
item.o.remove();
link(item.prev, item.next); link(item.prev, item.next);
} }
destroy_effect(item.e, !is_controlled); destroy_effect(item.e, !is_controlled);
@ -182,11 +181,10 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
break; break;
} }
var child_open = /** @type {Comment} */ (child_anchor);
child_anchor = hydrate_anchor(child_anchor); child_anchor = hydrate_anchor(child_anchor);
var value = array[i]; var value = array[i];
var key = get_key(value, i); var key = get_key(value, i);
item = create_item(child_open, child_anchor, prev, null, value, key, i, render_fn, flags); item = create_item(child_anchor, prev, null, value, key, i, render_fn, flags);
state.items.set(key, item); state.items.set(key, item);
child_anchor = /** @type {Comment} */ (child_anchor.nextSibling); child_anchor = /** @type {Comment} */ (child_anchor.nextSibling);
@ -293,22 +291,9 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
item = items.get(key); item = items.get(key);
if (item === undefined) { if (item === undefined) {
var child_open = empty(); var child_anchor = current ? get_first_node(current.e) : anchor;
var child_anchor = current ? current.o : anchor;
child_anchor.before(child_open); prev = create_item(child_anchor, prev, prev.next, value, key, i, render_fn, flags);
prev = create_item(
child_open,
child_anchor,
prev,
prev.next,
value,
key,
i,
render_fn,
flags
);
items.set(key, prev); items.set(key, prev);
@ -451,7 +436,6 @@ function update_item(item, value, index, type) {
/** /**
* @template V * @template V
* @param {Comment | Text} open
* @param {Node} anchor * @param {Node} anchor
* @param {import('#client').EachItem | import('#client').EachState} prev * @param {import('#client').EachItem | import('#client').EachState} prev
* @param {import('#client').EachItem | null} next * @param {import('#client').EachItem | null} next
@ -462,7 +446,7 @@ function update_item(item, value, index, type) {
* @param {number} flags * @param {number} flags
* @returns {import('#client').EachItem} * @returns {import('#client').EachItem}
*/ */
function create_item(open, anchor, prev, next, value, key, index, render_fn, flags) { function create_item(anchor, prev, next, value, key, index, render_fn, flags) {
var previous_each_item = current_each_item; var previous_each_item = current_each_item;
try { try {
@ -480,7 +464,6 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla
a: null, a: null,
// @ts-expect-error // @ts-expect-error
e: null, e: null,
o: open,
prev, prev,
next next
}; };
@ -497,16 +480,52 @@ function create_item(open, anchor, prev, next, value, key, index, render_fn, fla
} }
} }
/**
* @param {import('#client').TemplateNode} dom
* @param {import("#client").Effect} effect
* @returns {import('#client').TemplateNode}
*/
function get_adjusted_first_node(dom, effect) {
if ((dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') || dom.nodeType === 8) {
var adjusted = effect.first;
var next;
while (adjusted !== null) {
next = adjusted.first;
if (adjusted.dom !== null) {
break;
} else if (next === null) {
return /** @type {import('#client').TemplateNode} */ (dom.previousSibling);
}
adjusted = next;
}
return get_first_node(/** @type {import("#client").Effect} */ (adjusted));
}
return dom;
}
/**
*
* @param {import('#client').Effect} effect
* @returns {import('#client').TemplateNode}
*/
function get_first_node(effect) {
var dom = effect.dom;
if (is_array(dom)) {
return get_adjusted_first_node(dom[0], effect);
}
return get_adjusted_first_node(/** @type {import('#client').TemplateNode} **/ (dom), effect);
}
/** /**
* @param {import('#client').EachItem} item * @param {import('#client').EachItem} item
* @param {import('#client').EachItem | null} next * @param {import('#client').EachItem | null} next
* @param {Text | Element | Comment} anchor * @param {Text | Element | Comment} anchor
*/ */
function move(item, next, anchor) { function move(item, next, anchor) {
var end = item.next ? item.next.o : anchor; var end = item.next ? get_first_node(item.next.e) : anchor;
var dest = next ? next.o : anchor; var dest = next ? get_first_node(next.e) : anchor;
var node = /** @type {import('#client').TemplateNode} */ (item.o); var node = get_first_node(item.e);
while (node !== end) { while (node !== end) {
var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling); var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling);

@ -47,7 +47,6 @@ function swap_block_dom(effect, from, to) {
* @returns {void} * @returns {void}
*/ */
export function element(node, get_tag, is_svg, render_fn, get_namespace, location) { export function element(node, get_tag, is_svg, render_fn, get_namespace, location) {
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
const filename = DEV && location && current_component_context?.function.filename; const filename = DEV && location && current_component_context?.function.filename;
/** @type {string | null} */ /** @type {string | null} */
@ -64,6 +63,18 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
/** @type {import('#client').Effect | null} */ /** @type {import('#client').Effect | null} */
let effect; let effect;
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
// Remove the the hydrated effect dom entry for our dynamic element
if (hydrating && is_array(parent_effect.dom)) {
var remove_index = parent_effect.dom.indexOf(
/** @type {import('#client').TemplateNode} */ (element)
);
if (remove_index !== -1) {
parent_effect.dom.splice(remove_index, 1);
}
}
/** /**
* The keyed `{#each ...}` item block, if any, that this element is inside. * The keyed `{#each ...}` item block, if any, that this element is inside.
* We track this so we can set it when changing the element, allowing any * 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
let each_item_block = current_each_item; let each_item_block = current_each_item;
block(() => { block(() => {
const element_effect = /** @type {import('#client').Effect} */ (current_effect);
const next_tag = get_tag() || null; const next_tag = get_tag() || null;
const ns = get_namespace const ns = get_namespace
? get_namespace() ? get_namespace()
@ -124,6 +136,13 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
}; };
} }
if (prev_element && !hydrating) {
swap_block_dom(element_effect, prev_element, element);
prev_element.remove();
} else {
push_template_node(element, element_effect);
}
if (render_fn) { if (render_fn) {
// If hydrating, use the existing ssr comment as the anchor so that the // If hydrating, use the existing ssr comment as the anchor so that the
// inner open and close methods can pick up the existing nodes correctly // 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
anchor.before(element); anchor.before(element);
if (!hydrating) {
if (prev_element) {
swap_block_dom(parent_effect, prev_element, element);
prev_element.remove();
} else {
push_template_node(element, parent_effect);
}
}
// See below // See below
return noop; return noop;
}); });

@ -85,8 +85,6 @@ export type EachItem = {
i: number | Source<number>; i: number | Source<number>;
/** key */ /** key */
k: unknown; k: unknown;
/** anchor for items inserted before this */
o: Comment | Text;
prev: EachItem | EachState; prev: EachItem | EachState;
next: EachItem | null; next: EachItem | null;
}; };

Loading…
Cancel
Save