From 563fc88cd88808eb46d58cb410fe629db4bc6ad4 Mon Sep 17 00:00:00 2001 From: tanhauhau Date: Sat, 16 Apr 2022 01:04:56 +0800 Subject: [PATCH] better hydration for only html tag --- .../compile/render_dom/wrappers/Element/index.ts | 12 +++++++----- .../compile/render_dom/wrappers/RawMustacheTag.ts | 3 ++- src/compiler/compile/render_ssr/Renderer.ts | 1 + .../compile/render_ssr/handlers/Element.ts | 8 +++++--- .../compile/render_ssr/handlers/HtmlTag.ts | 4 ++-- test/runtime/index.ts | 7 +++++++ .../raw-anchor-first-last-child/_config.js | 15 ++++++++++++++- .../samples/raw-mustaches-preserved/_config.js | 2 +- 8 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 2ba57c5343ed..bda915aa7c15 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -363,13 +363,15 @@ export default class ElementWrapper extends Wrapper { 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 (parent_nodes) { 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` var ${nodes} = ${children}; `); @@ -477,7 +479,7 @@ export default class ElementWrapper extends Wrapper { this.add_styles(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( b`${this.node.children.length > 0 ? nodes : children}.forEach(@detach);` ); @@ -513,7 +515,7 @@ export default class ElementWrapper extends Wrapper { 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 .filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name) .map((attr) => p`${(attr as StyleAttributeWrapper | AttributeWrapper).name}: true`); @@ -531,7 +533,7 @@ export default class ElementWrapper extends Wrapper { 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`); } diff --git a/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts b/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts index 229f57923858..1ec5aac96f86 100644 --- a/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts +++ b/src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts @@ -7,6 +7,7 @@ import MustacheTag from '../../nodes/MustacheTag'; import RawMustacheTag from '../../nodes/RawMustacheTag'; import { is_head } from './shared/is_head'; import { Identifier } from 'estree'; +import hash from '../../utils/hash'; export default class RawMustacheTagWrapper extends Tag { var: Identifier = { type: 'Identifier', name: 'raw' }; @@ -33,7 +34,7 @@ export default class RawMustacheTagWrapper extends Tag { 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 { const needs_anchor = in_head || (this.next ? !this.next.is_dom_node() : (!this.parent || !this.parent.is_dom_node())); diff --git a/src/compiler/compile/render_ssr/Renderer.ts b/src/compiler/compile/render_ssr/Renderer.ts index d9f9c2e635af..6cdfd6adb545 100644 --- a/src/compiler/compile/render_ssr/Renderer.ts +++ b/src/compiler/compile/render_ssr/Renderer.ts @@ -47,6 +47,7 @@ export interface RenderOptions extends CompileOptions{ locate: (c: number) => { line: number; column: number }; head_id?: string; has_added_svelte_hash?: boolean; + optimised_html_hydration?: boolean; } export default class Renderer { diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index c8c1e34fa2d6..aa56621ff08c 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -10,6 +10,7 @@ import fix_attribute_casing from '../../render_dom/wrappers/Element/fix_attribut import { namespaces } from '../../../utils/namespaces'; import { start_newline } from '../../../utils/patterns'; import { Expression as ESExpression } from 'estree'; +import hash from '../../utils/hash'; export default function (node: Element, renderer: Renderer, options: RenderOptions) { @@ -156,9 +157,10 @@ export default function (node: Element, renderer: Renderer, options: RenderOptio if (options.hydratable) { if (options.head_id) { renderer.add_string(` data-svelte="${options.head_id}"`); - } - - if (node.can_optimise_to_html_string && !options.has_added_svelte_hash) { + } else if (node.children.length === 1 && node.children[0].type === 'RawMustacheTag') { + renderer.add_string(` data-svelte="${hash(JSON.stringify(node.children[0].expression.node))}"`); + 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()}"`); options = { ...options, has_added_svelte_hash: true }; } diff --git a/src/compiler/compile/render_ssr/handlers/HtmlTag.ts b/src/compiler/compile/render_ssr/handlers/HtmlTag.ts index cd62b959818e..89ec16df8cdf 100644 --- a/src/compiler/compile/render_ssr/handlers/HtmlTag.ts +++ b/src/compiler/compile/render_ssr/handlers/HtmlTag.ts @@ -3,7 +3,7 @@ import RawMustacheTag from '../../nodes/RawMustacheTag'; import { Expression } from 'estree'; export default function(node: RawMustacheTag, renderer: Renderer, options: RenderOptions) { - if (options.hydratable) renderer.add_string(''); + if (options.hydratable && !options.optimised_html_hydration) renderer.add_string(''); renderer.add_expression(node.expression.node as Expression); - if (options.hydratable) renderer.add_string(''); + if (options.hydratable && !options.optimised_html_hydration) renderer.add_string(''); } diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 0e445b7a7115..3b39fe54e033 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -154,6 +154,7 @@ describe('runtime', () => { window.SvelteComponent = SvelteComponent; const target = window.document.querySelector('main'); + let snapshot = undefined; if (hydrate && from_ssr_html) { // ssr into target @@ -163,6 +164,11 @@ describe('runtime', () => { const SsrSvelteComponent = require(`./samples/${dir}/main.svelte`).default; const { html } = SsrSvelteComponent.render(config.props); target.innerHTML = html; + + if (config.snapshot) { + snapshot = config.snapshot(target); + } + delete compileOptions.generate; if (config.after_test) config.after_test(); } else { @@ -212,6 +218,7 @@ describe('runtime', () => { component, mod, target, + snapshot, window, raf, compileOptions diff --git a/test/runtime/samples/raw-anchor-first-last-child/_config.js b/test/runtime/samples/raw-anchor-first-last-child/_config.js index 30acfe4d0f2b..3c7ada1998b4 100644 --- a/test/runtime/samples/raw-anchor-first-last-child/_config.js +++ b/test/runtime/samples/raw-anchor-first-last-child/_config.js @@ -3,11 +3,24 @@ export default { raw: 'foo' }, - test({ assert, component, target }) { + snapshot(target) { + const span = target.querySelector('span'); + + return { + span + }; + }, + + test({ assert, component, target, snapshot }) { const span = target.querySelector('span'); assert.ok(!span.previousSibling); assert.ok(!span.nextSibling); + if (snapshot) { + assert.equal(span, snapshot.span); + } + component.raw = 'bar'; + assert.htmlEqual(target.innerHTML, '
bar
'); } }; diff --git a/test/runtime/samples/raw-mustaches-preserved/_config.js b/test/runtime/samples/raw-mustaches-preserved/_config.js index d4d57025d2ac..184bb6fe4d0b 100644 --- a/test/runtime/samples/raw-mustaches-preserved/_config.js +++ b/test/runtime/samples/raw-mustaches-preserved/_config.js @@ -11,7 +11,7 @@ export default { const p = target.querySelector('p'); component.raw = '

does not change

'; - assert.equal(target.innerHTML, '

does not change

'); + assert.htmlEqual(target.innerHTML, '

does not change

'); assert.strictEqual(target.querySelector('p'), p); } };