only attach SSR <head> markers when hydratable: true (#4260)

pull/4275/head
Conduitry 5 years ago committed by GitHub
parent c97e8f81db
commit 7494509dfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,9 @@
# Svelte changelog
## Unreleased
* Only attach SSR mode markers to a component's `<head>` elements when compiling with `hydratable: true` ([#4258](https://github.com/sveltejs/svelte/issues/4258))
## 3.17.0
* Remove old `<head>` elements during hydration so they aren't duplicated ([#1607](https://github.com/sveltejs/svelte/issues/1607))

@ -887,7 +887,7 @@ Existing children of `target` are left where they are.
---
The `hydrate` option instructs Svelte to upgrade existing DOM (usually from server-side rendering) rather than creating new elements. It will only work if the component was compiled with the [`hydratable: true` option](docs#svelte_compile).
The `hydrate` option instructs Svelte to upgrade existing DOM (usually from server-side rendering) rather than creating new elements. It will only work if the component was compiled with the [`hydratable: true` option](docs#svelte_compile). Hydration of `<head>` elements only works properly if the server-side rendering code was also compiled with `hydratable: true`, which adds a marker to each element in the `<head>` so that the component knows which elements it's responsible for removing during hydration.
Whereas children of `target` are normally left alone, `hydrate: true` will cause any children to be removed. For that reason, the `anchor` option cannot be used alongside `hydrate: true`.

@ -68,7 +68,7 @@ The following options can be passed to the compiler. None are required:
| `generate` | `"dom"` | If `"dom"`, Svelte emits a JavaScript class for mounting to the DOM. If `"ssr"`, Svelte emits an object with a `render` method suitable for server-side rendering. If `false`, no JavaScript or CSS is returned; just metadata.
| `dev` | `false` | If `true`, causes extra code to be added to components that will perform runtime checks and provide debugging information during development.
| `immutable` | `false` | If `true`, tells the compiler that you promise not to mutate any objects. This allows it to be less conservative about checking whether values have changed.
| `hydratable` | `false` | If `true`, enables the `hydrate: true` runtime option, which allows a component to upgrade existing DOM rather than creating new DOM from scratch.
| `hydratable` | `false` | If `true` when generating DOM code, enables the `hydrate: true` runtime option, which allows a component to upgrade existing DOM rather than creating new DOM from scratch. When generating SSR code, this adds markers to `<head>` elements so that hydration knows which to replace.
| `legacy` | `false` | If `true`, generates code that will work in IE9 and IE10, which don't support things like `element.dataset`.
| `accessors` | `false` | If `true`, getters and setters will be created for the component's props. If `false`, they will only be created for readonly exported values (i.e. those declared with `const`, `class` and `function`). If compiling with `customElement: true` this option defaults to `true`.
| `customElement` | `false` | If `true`, tells the compiler to generate a custom element constructor instead of a regular Svelte component.

@ -124,7 +124,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
}
});
if (options.head_id) {
if (options.hydratable && options.head_id) {
renderer.add_string(` data-svelte="${options.head_id}"`);
}

@ -5,7 +5,11 @@ import { x } from 'code-red';
export default function(node: Title, renderer: Renderer, options: RenderOptions) {
renderer.push();
renderer.add_string(`<title data-svelte="${options.head_id}">`);
renderer.add_string('<title');
if (options.hydratable && options.head_id) {
renderer.add_string(` data-svelte="${options.head_id}"`);
}
renderer.add_string('>');
renderer.render(node.children, options);

@ -45,6 +45,12 @@ export function tryToReadFile(file) {
}
}
export function cleanRequireCache() {
Object.keys(require.cache)
.filter(x => x.endsWith('.svelte'))
.forEach(file => delete require.cache[file]);
}
const virtualConsole = new jsdom.VirtualConsole();
virtualConsole.sendTo(console);

@ -10,6 +10,7 @@ import {
showOutput,
loadConfig,
loadSvelte,
cleanRequireCache,
env,
setupHtmlEqual,
mkdirp
@ -79,11 +80,7 @@ describe("runtime", () => {
compileOptions.immutable = config.immutable;
compileOptions.accessors = 'accessors' in config ? config.accessors : true;
Object.keys(require.cache)
.filter(x => x.endsWith('.svelte'))
.forEach(file => {
delete require.cache[file];
});
cleanRequireCache();
let mod;
let SvelteComponent;

@ -9,6 +9,7 @@ import {
loadSvelte,
setupHtmlEqual,
tryToLoadJson,
cleanRequireCache,
shouldUpdateExpected,
mkdirp
} from "../helpers.js";
@ -27,11 +28,6 @@ let compile = null;
describe("ssr", () => {
before(() => {
require("../../register")({
extensions: ['.svelte', '.html'],
sveltePath
});
compile = loadSvelte(true).compile;
return setupHtmlEqual();
@ -40,9 +36,11 @@ describe("ssr", () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
if (dir[0] === ".") return;
const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`);
// add .solo to a sample directory name to only run that test, or
// .show to always show the output. or both
const solo = /\.solo/.test(dir);
const solo = config.solo || /\.solo/.test(dir);
const show = /\.show/.test(dir);
if (solo && process.env.CI) {
@ -51,6 +49,18 @@ describe("ssr", () => {
(solo ? it.only : it)(dir, () => {
dir = path.resolve(`${__dirname}/samples`, dir);
cleanRequireCache();
const compileOptions = {
sveltePath,
...config.compileOptions,
generate: 'ssr',
format: 'cjs'
};
require("../../register")(compileOptions);
try {
const Component = require(`${dir}/main.svelte`).default;
@ -133,18 +143,16 @@ describe("ssr", () => {
(config.skip ? it.skip : solo ? it.only : it)(dir, () => {
const cwd = path.resolve("test/runtime/samples", dir);
Object.keys(require.cache)
.filter(x => x.endsWith('.svelte'))
.forEach(file => {
delete require.cache[file];
});
cleanRequireCache();
delete global.window;
const compileOptions = Object.assign({ sveltePath }, config.compileOptions, {
const compileOptions = {
sveltePath,
...config.compileOptions,
generate: 'ssr',
format: 'cjs'
});
};
require("../../register")(compileOptions);

@ -0,0 +1,5 @@
export default {
compileOptions: {
hydratable: true
}
};

@ -1 +1 @@
<title data-svelte="svelte-1csszk6">B</title>
<title>B</title>

@ -1 +1 @@
<title data-svelte="svelte-135agoq">a custom title</title>
<title>a custom title</title>
Loading…
Cancel
Save