From 97d77d6918642721d0bba5cbd3dfac70d88d1bb7 Mon Sep 17 00:00:00 2001 From: Mathias Picker <48158184+MathiasWP@users.noreply.github.com> Date: Wed, 20 May 2026 15:27:36 +0200 Subject: [PATCH] perf: store setters cache as Set instead of Array The per-element-type setters cache (`setters_cache`) in `attributes.js` stored arrays and searched them with `Array.prototype.includes`, which is O(n). Element prototypes have 30-50 setters, and the cache is consulted on every spread-attribute assignment and on every non-string attribute write. Switching the cache to `Set` makes each lookup O(1) without changing any behaviour. --- .changeset/setters-cache-set.md | 5 +++++ .../src/internal/client/dom/elements/attributes.js | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/setters-cache-set.md diff --git a/.changeset/setters-cache-set.md b/.changeset/setters-cache-set.md new file mode 100644 index 0000000000..2013446bc5 --- /dev/null +++ b/.changeset/setters-cache-set.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: store setters cache as `Set` instead of `Array` diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index a193a70dd5..67f73d1e3f 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -201,7 +201,7 @@ export function set_attribute(element, attribute, value, skip_warning) { if (value == null) { element.removeAttribute(attribute); - } else if (typeof value !== 'string' && get_setters(element).includes(attribute)) { + } else if (typeof value !== 'string' && get_setters(element).has(attribute)) { // @ts-ignore element[attribute] = value; } else { @@ -252,7 +252,7 @@ export function set_custom_element_data(node, prop, value) { // customElements may not be available in browser extension contexts !customElements || customElements.get(node.getAttribute('is') || node.nodeName.toLowerCase()) - ? get_setters(node).includes(prop) + ? get_setters(node).has(prop) : value && typeof value === 'object') ) { // @ts-expect-error @@ -463,7 +463,7 @@ function set_attributes( } } else if ( is_default || - (setters.includes(name) && (is_custom_element || typeof value !== 'string')) + (setters.has(name) && (is_custom_element || typeof value !== 'string')) ) { // @ts-ignore element[name] = value; @@ -572,7 +572,7 @@ function get_attributes(element) { ); } -/** @type {Map} */ +/** @type {Map>} */ var setters_cache = new Map(); /** @param {Element} element */ @@ -580,7 +580,7 @@ function get_setters(element) { var cache_key = element.getAttribute('is') || element.nodeName; var setters = setters_cache.get(cache_key); if (setters) return setters; - setters_cache.set(cache_key, (setters = [])); + setters_cache.set(cache_key, (setters = new Set())); var descriptors; var proto = element; // In the case of custom elements there might be setters on the instance @@ -599,7 +599,7 @@ function get_setters(element) { key !== 'textContent' && key !== 'innerText' ) { - setters.push(key); + setters.add(key); } }