feat: ignore href when hydrating (#9662)

* ignore href when hydrating

* remove unused export keyword

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/9667/head
Rich Harris 1 year ago committed by GitHub
parent da1aa7c4a8
commit 9c44fd7854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
feat: ignore href attributes when hydrating

@ -2532,6 +2532,7 @@ export function attr(dom, attribute, value) {
// (we can't just compare the strings as they can be different between client and server but result in the // (we can't just compare the strings as they can be different between client and server but result in the
// same url, so we would need to create hidden anchor elements to compare them) // same url, so we would need to create hidden anchor elements to compare them)
attribute !== 'src' && attribute !== 'src' &&
attribute !== 'href' &&
attribute !== 'srcset') attribute !== 'srcset')
) { ) {
if (value === null) { if (value === null) {
@ -2550,7 +2551,7 @@ let src_url_equal_anchor;
* @param {string} url * @param {string} url
* @returns {boolean} * @returns {boolean}
*/ */
export function src_url_equal(element_src, url) { function src_url_equal(element_src, url) {
if (element_src === url) return true; if (element_src === url) return true;
if (!src_url_equal_anchor) { if (!src_url_equal_anchor) {
src_url_equal_anchor = document.createElement('a'); src_url_equal_anchor = document.createElement('a');
@ -2566,13 +2567,13 @@ function split_srcset(srcset) {
} }
/** /**
* @param {HTMLSourceElement | HTMLImageElement} element_srcset * @param {HTMLSourceElement | HTMLImageElement} element
* @param {string | undefined | null} srcset * @param {string | undefined | null} srcset
* @returns {boolean} * @returns {boolean}
*/ */
export function srcset_url_equal(element_srcset, srcset) { export function srcset_url_equal(element, srcset) {
const element_urls = split_srcset(element_srcset.srcset); const element_urls = split_srcset(element.srcset);
const urls = split_srcset(srcset || ''); const urls = split_srcset(srcset ?? '');
return ( return (
urls.length === element_urls.length && urls.length === element_urls.length &&
@ -2595,23 +2596,21 @@ export function srcset_url_equal(element_srcset, srcset) {
* @param {string | null} value * @param {string | null} value
*/ */
function check_src_in_dev_hydration(dom, attribute, value) { function check_src_in_dev_hydration(dom, attribute, value) {
if (current_hydration_fragment !== null && (attribute === 'src' || attribute === 'srcset')) { if (!current_hydration_fragment) return;
if ( if (attribute !== 'src' && attribute !== 'href' && attribute !== 'srcset') return;
(attribute === 'src' && !src_url_equal(dom.getAttribute('src') || '', value || '')) ||
(attribute === 'srcset' && if (attribute === 'srcset' && srcset_url_equal(dom, value)) return;
!srcset_url_equal(/** @type {HTMLImageElement | HTMLSourceElement} */ (dom), value || '')) if (src_url_equal(dom.getAttribute(attribute) ?? '', value ?? '')) return;
) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error( console.error(
'Detected a src/srcset attribute value change during hydration. This will not be repaired during hydration, ' + `Detected a ${attribute} attribute value change during hydration. This will not be repaired during hydration, ` +
'the src/srcset value that came from the server will be used. Related element:', `the ${attribute} value that came from the server will be used. Related element:`,
dom, dom,
' Differing value:', ' Differing value:',
value value
); );
} }
}
}
/** /**
* @param {Element} dom * @param {Element} dom
@ -2778,7 +2777,7 @@ export function spread_attributes(dom, prev, attrs, lowercase_attributes, css_ha
if ( if (
current_hydration_fragment === null || current_hydration_fragment === null ||
// @ts-ignore see attr method for an explanation of src/srcset // @ts-ignore see attr method for an explanation of src/srcset
(dom[name] !== value && name !== 'src' && name !== 'srcset') (dom[name] !== value && name !== 'src' && name !== 'href' && name !== 'srcset')
) { ) {
// @ts-ignore // @ts-ignore
dom[name] = value; dom[name] = value;

@ -0,0 +1 @@
<!--ssr:0--><a href="/bar">foo</a><!--ssr:0-->

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
test(assert, target) {
assert.equal(target.querySelector('a')?.getAttribute('href'), '/bar');
}
});

@ -0,0 +1,5 @@
<script>
let browser = typeof window !== 'undefined';
</script>
<a href={browser ? '/foo': '/bar'}>foo</a>
Loading…
Cancel
Save