better hydration for only html tag

pull/7426/head
tanhauhau 4 years ago
parent 64b2e16fbc
commit 2aab40cc55

@ -415,13 +415,15 @@ export default class ElementWrapper extends Wrapper {
const { can_use_textcontent, can_optimise_to_html_string } = this.node; const { can_use_textcontent, can_optimise_to_html_string } = this.node;
const to_optimise_hydration = can_optimise_to_html_string || (!is_head(parent_node) && this.node.children.length === 1 && this.node.children[0].type === 'RawMustacheTag');
if (hydratable) { if (hydratable) {
if (parent_nodes) { if (parent_nodes) {
block.chunks.claim.push(b` block.chunks.claim.push(b`
${node} = ${this.get_claim_statement(block, parent_nodes, can_optimise_to_html_string)}; ${node} = ${this.get_claim_statement(block, parent_nodes, to_optimise_hydration)};
`); `);
if (!can_optimise_to_html_string && !this.void && this.node.children.length > 0) { if (!to_optimise_hydration && !this.void && this.node.children.length > 0) {
block.chunks.claim.push(b` block.chunks.claim.push(b`
var ${nodes} = ${children}; var ${nodes} = ${children};
`); `);
@ -534,7 +536,7 @@ export default class ElementWrapper extends Wrapper {
this.add_styles(block); this.add_styles(block);
this.add_manual_style_scoping(block); this.add_manual_style_scoping(block);
if (nodes && hydratable && !this.void && !can_optimise_to_html_string) { if (nodes && hydratable && !this.void && !to_optimise_hydration) {
block.chunks.claim.push( block.chunks.claim.push(
b`${this.node.children.length > 0 ? nodes : children}.forEach(@detach);` b`${this.node.children.length > 0 ? nodes : children}.forEach(@detach);`
); );
@ -570,7 +572,7 @@ export default class ElementWrapper extends Wrapper {
return x`@element(${reference})`; return x`@element(${reference})`;
} }
get_claim_statement(block: Block, nodes: Identifier, can_optimise_to_html_string: boolean) { get_claim_statement(block: Block, nodes: Identifier, to_optimise_hydration: boolean) {
const attributes = this.attributes const attributes = this.attributes
.filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name) .filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name)
.map((attr) => p`${(attr as StyleAttributeWrapper | AttributeWrapper).name}: true`); .map((attr) => p`${(attr as StyleAttributeWrapper | AttributeWrapper).name}: true`);
@ -588,7 +590,7 @@ export default class ElementWrapper extends Wrapper {
reference = x`(${this.node.tag_expr.manipulate(block)} || 'null').toUpperCase()`; reference = x`(${this.node.tag_expr.manipulate(block)} || 'null').toUpperCase()`;
} }
if (can_optimise_to_html_string) { if (to_optimise_hydration) {
attributes.push(p`["data-svelte"]: true`); attributes.push(p`["data-svelte"]: true`);
} }

@ -9,6 +9,7 @@ import MustacheTag from '../../nodes/MustacheTag';
import RawMustacheTag from '../../nodes/RawMustacheTag'; import RawMustacheTag from '../../nodes/RawMustacheTag';
import { is_head } from './shared/is_head'; import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree'; import { Identifier, Node } from 'estree';
import hash from '../../utils/hash';
export default class RawMustacheTagWrapper extends Tag { export default class RawMustacheTagWrapper extends Tag {
var: Identifier = { type: 'Identifier', name: 'raw' }; var: Identifier = { type: 'Identifier', name: 'raw' };
@ -35,7 +36,7 @@ export default class RawMustacheTagWrapper extends Tag {
content => insert(content) content => insert(content)
); );
block.chunks.mount.push(insert(init)); block.chunks.mount.push(b`if (@get_svelte_dataset(${parent_node}) !== "${hash(JSON.stringify(this.node.expression.node))}") ${insert(init)}`);
} else { } else {
const needs_anchor = in_head || (this.next ? !this.next.is_dom_node() : (!this.parent || !this.parent.is_dom_node())); const needs_anchor = in_head || (this.next ? !this.next.is_dom_node() : (!this.parent || !this.parent.is_dom_node()));

@ -48,6 +48,7 @@ export interface RenderOptions extends CompileOptions{
locate: (c: number) => { line: number; column: number }; locate: (c: number) => { line: number; column: number };
head_id?: string; head_id?: string;
has_added_svelte_hash?: boolean; has_added_svelte_hash?: boolean;
optimised_html_hydration?: boolean;
} }
export default class Renderer { export default class Renderer {

@ -10,6 +10,7 @@ import fix_attribute_casing from '../../render_dom/wrappers/Element/fix_attribut
import { namespaces } from '../../../utils/namespaces'; import { namespaces } from '../../../utils/namespaces';
import { regex_starts_with_newline } from '../../../utils/patterns'; import { regex_starts_with_newline } from '../../../utils/patterns';
import { Node, Expression as ESExpression } from 'estree'; import { Node, Expression as ESExpression } from 'estree';
import hash from '../../utils/hash';
export default function (node: Element, renderer: Renderer, options: RenderOptions) { export default function (node: Element, renderer: Renderer, options: RenderOptions) {
@ -163,9 +164,10 @@ export default function (node: Element, renderer: Renderer, options: RenderOptio
if (options.hydratable) { if (options.hydratable) {
if (options.head_id) { if (options.head_id) {
renderer.add_string(` data-svelte="${options.head_id}"`); renderer.add_string(` data-svelte="${options.head_id}"`);
} } else if (node.children.length === 1 && node.children[0].type === 'RawMustacheTag') {
renderer.add_string(` data-svelte="${hash(JSON.stringify(node.children[0].expression.node))}"`);
if (node.can_optimise_to_html_string && !options.has_added_svelte_hash) { options = { ...options, optimised_html_hydration: true };
} else if (node.can_optimise_to_html_string && !options.has_added_svelte_hash) {
renderer.add_string(` data-svelte="${node.hash()}"`); renderer.add_string(` data-svelte="${node.hash()}"`);
options = { ...options, has_added_svelte_hash: true }; options = { ...options, has_added_svelte_hash: true };
} }

@ -3,7 +3,7 @@ import RawMustacheTag from '../../nodes/RawMustacheTag';
import { Expression } from 'estree'; import { Expression } from 'estree';
export default function(node: RawMustacheTag, renderer: Renderer, options: RenderOptions) { export default function(node: RawMustacheTag, renderer: Renderer, options: RenderOptions) {
if (options.hydratable) renderer.add_string('<!-- HTML_TAG_START -->'); if (options.hydratable && !options.optimised_html_hydration) renderer.add_string('<!-- HTML_TAG_START -->');
renderer.add_expression(node.expression.node as Expression); renderer.add_expression(node.expression.node as Expression);
if (options.hydratable) renderer.add_string('<!-- HTML_TAG_END -->'); if (options.hydratable && !options.optimised_html_hydration) renderer.add_string('<!-- HTML_TAG_END -->');
} }

@ -154,6 +154,7 @@ describe('runtime', () => {
window.SvelteComponent = SvelteComponent; window.SvelteComponent = SvelteComponent;
const target = window.document.querySelector('main'); const target = window.document.querySelector('main');
let snapshot = undefined;
if (hydrate && from_ssr_html) { if (hydrate && from_ssr_html) {
// ssr into target // ssr into target
@ -163,6 +164,11 @@ describe('runtime', () => {
const SsrSvelteComponent = require(`./samples/${dir}/main.svelte`).default; const SsrSvelteComponent = require(`./samples/${dir}/main.svelte`).default;
const { html } = SsrSvelteComponent.render(config.props); const { html } = SsrSvelteComponent.render(config.props);
target.innerHTML = html; target.innerHTML = html;
if (config.snapshot) {
snapshot = config.snapshot(target);
}
delete compileOptions.generate; delete compileOptions.generate;
if (config.after_test) config.after_test(); if (config.after_test) config.after_test();
} else { } else {
@ -212,6 +218,7 @@ describe('runtime', () => {
component, component,
mod, mod,
target, target,
snapshot,
window, window,
raf, raf,
compileOptions compileOptions

@ -3,11 +3,24 @@ export default {
raw: '<span>foo</span>' raw: '<span>foo</span>'
}, },
test({ assert, component, target }) { snapshot(target) {
const span = target.querySelector('span');
return {
span
};
},
test({ assert, component, target, snapshot }) {
const span = target.querySelector('span'); const span = target.querySelector('span');
assert.ok(!span.previousSibling); assert.ok(!span.previousSibling);
assert.ok(!span.nextSibling); assert.ok(!span.nextSibling);
if (snapshot) {
assert.equal(span, snapshot.span);
}
component.raw = '<span>bar</span>'; component.raw = '<span>bar</span>';
assert.htmlEqual(target.innerHTML, '<div><span>bar</span></div>');
} }
}; };

@ -11,7 +11,7 @@ export default {
const p = target.querySelector('p'); const p = target.querySelector('p');
component.raw = '<p>does not change</p>'; component.raw = '<p>does not change</p>';
assert.equal(target.innerHTML, '<div><p>does not change</p></div>'); assert.htmlEqual(target.innerHTML, '<div><p>does not change</p></div>');
assert.strictEqual(target.querySelector('p'), p); assert.strictEqual(target.querySelector('p'), p);
} }
}; };

Loading…
Cancel
Save