diff --git a/.changeset/create-element-fast-path.md b/.changeset/create-element-fast-path.md new file mode 100644 index 0000000000..8b88a14163 --- /dev/null +++ b/.changeset/create-element-fast-path.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: use `createElement` instead of `createElementNS` for HTML elements diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index f6d05162ca..bc12a0b07e 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -233,6 +233,12 @@ export function should_defer_append() { } /** + * Branching here is intentional and load-bearing for perf. `createElement(tag)` + * hits a fast path in Blink that `createElementNS(NAMESPACE_HTML, tag)` doesn't, + * and passing an explicit `undefined` as the trailing options arg measurably + * slows both APIs. Funnelling every case through a single `createElementNS(ns, + * tag, options)` call would be smaller but slower on the HTML path. + * * @template {keyof HTMLElementTagNameMap | string} T * @param {T} tag * @param {string} [namespace] @@ -240,9 +246,13 @@ export function should_defer_append() { * @returns {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */ export function create_element(tag, namespace, is) { - let options = is ? { is } : undefined; + if (namespace == null || namespace === NAMESPACE_HTML) { + return /** @type {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */ ( + is ? document.createElement(tag, { is }) : document.createElement(tag) + ); + } return /** @type {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */ ( - document.createElementNS(namespace ?? NAMESPACE_HTML, tag, options) + is ? document.createElementNS(namespace, tag, { is }) : document.createElementNS(namespace, tag) ); }