Skip to content

Commit 4a63168

Browse files
authored
feat: faster HTML tags (#10986)
* feat: faster HTML tags * move code to where it is used
1 parent cd90a5d commit 4a63168

File tree

3 files changed

+51
-38
lines changed

3 files changed

+51
-38
lines changed

.changeset/swift-poets-carry.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+
feat: faster HTML tags

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

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { derived } from '../../reactivity/deriveds.js';
22
import { render_effect } from '../../reactivity/effects.js';
33
import { get } from '../../runtime.js';
4-
import { reconcile_html, remove } from '../reconciler.js';
4+
import { hydrate_nodes, hydrating } from '../hydration.js';
5+
import { create_fragment_from_html, remove } from '../reconciler.js';
56

67
/**
78
* @param {Element | Text | Comment} anchor
@@ -13,10 +14,53 @@ export function html(anchor, get_value, svg) {
1314
let value = derived(get_value);
1415

1516
render_effect(() => {
16-
var dom = reconcile_html(anchor, get(value), svg);
17+
var dom = html_to_dom(anchor, get(value), svg);
1718

1819
if (dom) {
1920
return () => remove(dom);
2021
}
2122
});
2223
}
24+
25+
/**
26+
* Creates the content for a `@html` tag from its string value,
27+
* inserts it before the target anchor and returns the new nodes.
28+
* @template V
29+
* @param {Element | Text | Comment} target
30+
* @param {V} value
31+
* @param {boolean} svg
32+
* @returns {Element | Comment | (Element | Comment | Text)[]}
33+
*/
34+
function html_to_dom(target, value, svg) {
35+
if (hydrating) return hydrate_nodes;
36+
37+
var html = value + '';
38+
if (svg) html = `<svg>${html}</svg>`;
39+
40+
// Don't use create_fragment_with_script_from_html here because that would mean script tags are executed.
41+
// @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons.
42+
/** @type {DocumentFragment | Element} */
43+
var node = create_fragment_from_html(html);
44+
45+
if (svg) {
46+
node = /** @type {Element} */ (node.firstChild);
47+
}
48+
49+
if (node.childNodes.length === 1) {
50+
var child = /** @type {Text | Element | Comment} */ (node.firstChild);
51+
target.before(child);
52+
return child;
53+
}
54+
55+
var nodes = /** @type {Array<Text | Element | Comment>} */ ([...node.childNodes]);
56+
57+
if (svg) {
58+
while (node.firstChild) {
59+
target.before(node.firstChild);
60+
}
61+
} else {
62+
target.before(node);
63+
}
64+
65+
return nodes;
66+
}
Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { hydrate_nodes, hydrating } from './hydration.js';
21
import { is_array } from '../utils.js';
32

43
/** @param {string} html */
@@ -23,38 +22,3 @@ export function remove(current) {
2322
current.remove();
2423
}
2524
}
26-
27-
/**
28-
* Creates the content for a `@html` tag from its string value,
29-
* inserts it before the target anchor and returns the new nodes.
30-
* @template V
31-
* @param {Element | Text | Comment} target
32-
* @param {V} value
33-
* @param {boolean} svg
34-
* @returns {Element | Comment | (Element | Comment | Text)[]}
35-
*/
36-
export function reconcile_html(target, value, svg) {
37-
if (hydrating) {
38-
return hydrate_nodes;
39-
}
40-
var html = value + '';
41-
// Even if html is the empty string we need to continue to insert something or
42-
// else the element ordering gets out of sync, resulting in subsequent values
43-
// not getting inserted anymore.
44-
var frag_nodes;
45-
if (svg) {
46-
html = `<svg>${html}</svg>`;
47-
}
48-
// Don't use create_fragment_with_script_from_html here because that would mean script tags are executed.
49-
// @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons.
50-
var content = create_fragment_from_html(html);
51-
if (svg) {
52-
content = /** @type {DocumentFragment} */ (/** @type {unknown} */ (content.firstChild));
53-
}
54-
var clone = content.cloneNode(true);
55-
frag_nodes = [...clone.childNodes];
56-
frag_nodes.forEach((node) => {
57-
target.before(node);
58-
});
59-
return /** @type {Array<Text | Comment | Element>} */ (frag_nodes);
60-
}

0 commit comments

Comments
 (0)