repair hydration mismatches

svelte-html
Simon Holthausen 2 years ago
parent edf31f1ef0
commit c2f97c7894

@ -1,4 +1,5 @@
import { render_effect, teardown } from '../../reactivity/effects.js'; import { effect, render_effect, teardown } from '../../reactivity/effects.js';
import { untrack } from '../../runtime.js';
import { set_attribute } from '../elements/attributes.js'; import { set_attribute } from '../elements/attributes.js';
import { set_class } from '../elements/class.js'; import { set_class } from '../elements/class.js';
import { hydrating } from '../hydration.js'; import { hydrating } from '../hydration.js';
@ -15,10 +16,10 @@ export function svelte_html(get_attributes) {
// @ts-expect-error // @ts-expect-error
const current_setters = (node.__attributes_setters ??= {}); const current_setters = (node.__attributes_setters ??= {});
/** @type {Record<string, any>} */ /** @type {Record<string, any>} Does _not_ contain event listeners, those are handled separately */
let attributes; let attributes;
render_effect(() => { const set_html_attributes = () => {
attributes = get_attributes(); attributes = get_attributes();
for (const name in attributes) { for (const name in attributes) {
@ -29,9 +30,16 @@ export function svelte_html(get_attributes) {
let value = attributes[name]; let value = attributes[name];
current.push({ owner: self, value }); current.push({ owner: self, value });
// Do nothing on initial render during hydration: If there are attribute duplicates, the last value // Defer hydration on initial render during hydration: If there are attribute duplicates, the last value
// wins, which could result in needless hydration repairs from earlier values. // wins, so we wait until all values have been set to see if we're actually the last one that sets the value.
if (hydrating) continue; if (hydrating) {
effect(() => {
if (current[current.length - 1].owner === self) {
untrack(set_html_attributes);
}
});
return;
}
if (name === 'class') { if (name === 'class') {
// Avoid unrelated attribute changes from triggering class changes // Avoid unrelated attribute changes from triggering class changes
@ -42,7 +50,9 @@ export function svelte_html(get_attributes) {
set_attribute(node, name, value); set_attribute(node, name, value);
} }
} }
}); };
render_effect(set_html_attributes);
teardown(() => { teardown(() => {
for (const name in attributes) { for (const name in attributes) {

@ -0,0 +1,15 @@
import { test } from '../../test';
export default test({
server_props: {
lang: 'en'
},
props: {
lang: 'de'
},
test(assert, target) {
assert.htmlEqual(target.ownerDocument.documentElement.lang, 'de');
}
});

@ -0,0 +1,5 @@
<script>
let { lang } = $props();
</script>
<svelte:html {lang} />
Loading…
Cancel
Save