fast hydration

pull/4309/head
Avi Marcus 6 years ago
parent 2f81365e44
commit abd55510be

@ -250,7 +250,7 @@ export default class EachBlockWrapper extends Wrapper {
block.add_element( block.add_element(
update_anchor_node as Identifier, update_anchor_node as Identifier,
x`@empty()`, x`@empty()`,
parent_nodes && x`@empty()`, parent_nodes && x`@claim_text(${parent_nodes}, '')`,
parent_node parent_node
); );
} }
@ -366,7 +366,7 @@ export default class EachBlockWrapper extends Wrapper {
this.block.add_element( this.block.add_element(
this.block.first, this.block.first,
x`@empty()`, x`@empty()`,
parent_nodes && x`@empty()`, parent_nodes && x`@claim_text(#nodes, '')`,
null null
); );
} }

@ -8,7 +8,7 @@ import { escape_html, string_literal } from '../../../utils/stringify';
import TextWrapper from '../Text'; import TextWrapper from '../Text';
import TagWrapper from '../shared/Tag'; import TagWrapper from '../shared/Tag';
import fix_attribute_casing from './fix_attribute_casing'; import fix_attribute_casing from './fix_attribute_casing';
import { b, x, p } from 'code-red'; import { b, x } from 'code-red';
import { namespaces } from '../../../../utils/namespaces'; import { namespaces } from '../../../../utils/namespaces';
import AttributeWrapper from './Attribute'; import AttributeWrapper from './Attribute';
import StyleAttributeWrapper from './StyleAttribute'; import StyleAttributeWrapper from './StyleAttribute';
@ -386,7 +386,7 @@ export default class ElementWrapper extends Wrapper {
if (nodes && this.renderer.options.hydratable && !this.void) { if (nodes && this.renderer.options.hydratable && !this.void) {
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}.children.forEach(@detach);`
); );
} }
@ -422,17 +422,13 @@ export default class ElementWrapper extends Wrapper {
} }
get_claim_statement(nodes: Identifier) { get_claim_statement(nodes: Identifier) {
const attributes = this.node.attributes
.filter((attr) => attr.type === 'Attribute')
.map((attr) => p`${attr.name}: true`);
const name = this.node.namespace const name = this.node.namespace
? this.node.name ? this.node.name
: this.node.name.toUpperCase(); : this.node.name.toUpperCase();
const svg = this.node.namespace === namespaces.svg ? 1 : null; const svg = this.node.namespace === namespaces.svg ? 1 : null;
return x`@claim_element(${nodes}, "${name}", { ${attributes} }, ${svg})`; return x`@claim_element(${nodes}, "${name}", null, ${svg})`;
} }
add_directives_in_order (block: Block) { add_directives_in_order (block: Block) {

@ -43,7 +43,7 @@ export default class HeadWrapper extends Wrapper {
if (nodes && this.renderer.options.hydratable) { if (nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
b`${nodes}.forEach(@detach);` b`${nodes}.children.forEach(@detach);`
); );
} }
} }

@ -239,7 +239,7 @@ export default class IfBlockWrapper extends Wrapper {
block.add_element( block.add_element(
anchor as Identifier, anchor as Identifier,
x`@empty()`, x`@empty()`,
parent_nodes && x`@empty()`, parent_nodes && x`@claim_text(${parent_nodes}, '')/*IF242*/`,
parent_node parent_node
); );
} }

@ -22,7 +22,7 @@ export default class RawMustacheTagWrapper extends Tag {
this.not_static_content(); this.not_static_content();
} }
render(block: Block, parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
const in_head = is_head(parent_node); const in_head = is_head(parent_node);
const can_use_innerhtml = !in_head && parent_node && !this.prev && !this.next; const can_use_innerhtml = !in_head && parent_node && !this.prev && !this.next;
@ -53,11 +53,20 @@ export default class RawMustacheTagWrapper extends Tag {
const update_anchor = in_head ? 'null' : needs_anchor ? html_anchor : this.next ? this.next.var : 'null'; const update_anchor = in_head ? 'null' : needs_anchor ? html_anchor : this.next ? this.next.var : 'null';
block.chunks.hydrate.push(b`${html_tag} = new @HtmlTag(${init}, ${update_anchor});`); block.chunks.hydrate.push(b`${html_tag}.a = ${update_anchor};`);
block.chunks.mount.push(b`${html_tag}.m(${parent_node || '#target'}, ${parent_node ? null : 'anchor'});`); block.chunks.mount.push(b`${html_tag}.m(${parent_node || '#target'}, ${parent_node ? null : 'anchor'});`);
block.chunks.create.push(b`${html_tag} = new @HtmlTag(${init});`);
if (this.renderer.options.hydratable) {
block.chunks.claim.push(b`${html_tag} = new @HtmlTag(${init});`);
block.chunks.claim.push(
b`${html_tag}.l(${parent_nodes});`
);
}
if (needs_anchor) { if (needs_anchor) {
block.add_element(html_anchor, x`@empty()`, x`@empty()`, parent_node); block.add_element(html_anchor, x`@empty()`, x`@claim_text(${parent_nodes}, '')`, parent_node);
} }
if (!parent_node || in_head) { if (!parent_node || in_head) {

@ -63,7 +63,7 @@ export default class Wrapper {
block.add_element( block.add_element(
anchor, anchor,
x`@empty()`, x`@empty()`,
parent_nodes && x`@empty()`, parent_nodes && x`@claim_text(${parent_nodes}, '')`,
parent_node as Identifier parent_node as Identifier
); );
} }

@ -1,7 +1,7 @@
import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler'; import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler';
import { current_component, set_current_component } from './lifecycle'; import { current_component, set_current_component } from './lifecycle';
import { blank_object, is_function, run, run_all, noop } from './utils'; import { blank_object, is_function, run, run_all, noop } from './utils';
import { children } from './dom'; import { children, update_hydrating } from './dom';
import { transition_in } from './transitions'; import { transition_in } from './transitions';
interface Fragment { interface Fragment {
@ -146,6 +146,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
if (options.target) { if (options.target) {
if (options.hydrate) { if (options.hydrate) {
update_hydrating(true);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment!.l(children(options.target)); $$.fragment && $$.fragment!.l(children(options.target));
} else { } else {
@ -155,6 +156,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
if (options.intro) transition_in(component.$$.fragment); if (options.intro) transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor); mount_component(component, options.target, options.anchor);
update_hydrating(false);
flush(); flush();
} }

@ -1,12 +1,22 @@
import { has_prop } from "./utils"; import { has_prop } from "./utils";
let is_hydrating = false;
export function update_hydrating(val: boolean) {
is_hydrating = val;
}
export function append(target: Node, node: Node) { export function append(target: Node, node: Node) {
if (!is_hydrating || node.parentNode !== target) {
target.appendChild(node); target.appendChild(node);
} }
}
export function insert(target: Node, node: Node, anchor?: Node) { export function insert(target: Node, node: Node, anchor?: Node) {
if (!is_hydrating || node.parentNode !== target) {
target.insertBefore(node, anchor || null); target.insertBefore(node, anchor || null);
} }
}
export function detach(node: Node) { export function detach(node: Node) {
node.parentNode.removeChild(node); node.parentNode.removeChild(node);
@ -144,35 +154,48 @@ export function time_ranges_to_array(ranges) {
return array; return array;
} }
export function children(element) { export function children(element: HTMLElement) {
return Array.from(element.childNodes); const children = Array.from(element.childNodes);
return {
children,
element,
next: children[0] || null,
last: children.length ? children[children.length - 1].nextSibling : null,
};
} }
export function claim_element(nodes, name, attributes, svg) { export function claim_element(nodes, name, fallback, svg) {
for (let i = 0; i < nodes.length; i += 1) { for (let i = 0; i < nodes.children.length; i += 1) {
const node = nodes[i]; const node = nodes.children[i];
if (node.nodeType !== 3) {
if (node.nodeName === name) { if (node.nodeName === name) {
for (let j = 0; j < node.attributes.length; j += 1) { nodes.children.splice(0,i + 1);
const attribute = node.attributes[j]; nodes.next = nodes.children[0];
if (!attributes[attribute.name]) node.removeAttribute(attribute.name); return node;
} else {
nodes.next = nodes.last;
nodes.children.forEach(detach);
nodes.children.length = 0;
break;
} }
return nodes.splice(i, 1)[0]; // TODO strip unwanted attributes
} }
} }
const node = fallback || (svg ? svg_element(name) : element(name));
return svg ? svg_element(name) : element(name); insert(nodes.element, node, nodes.next);
return node;
} }
export function claim_text(nodes, data) { export function claim_text(nodes, data) {
for (let i = 0; i < nodes.length; i += 1) { if (nodes.children.length && nodes.children[0].nodeType === 3) {
const node = nodes[i]; const node = nodes.children.shift();
if (node.nodeType === 3) {
node.data = '' + data; node.data = '' + data;
return nodes.splice(i, 1)[0]; nodes.next = nodes.children[0];
} return node;
} else {
const node = text(data);
insert(nodes.element, node, nodes.next);
return node;
} }
return text(data);
} }
export function claim_space(nodes) { export function claim_space(nodes) {
@ -274,7 +297,13 @@ export function custom_event<T=any>(type: string, detail?: T) {
} }
export function query_selector_all(selector: string, parent: HTMLElement = document.body) { export function query_selector_all(selector: string, parent: HTMLElement = document.body) {
return Array.from(parent.querySelectorAll(selector)); const children = Array.from(parent.querySelectorAll(selector));
return {
children,
element: parent,
next: children[0] || null,
last: children.length ? children[children.length - 1].nextSibling : null
};
} }
export class HtmlTag { export class HtmlTag {
@ -283,9 +312,8 @@ export class HtmlTag {
t: HTMLElement; t: HTMLElement;
a: HTMLElement; a: HTMLElement;
constructor(html: string, anchor: HTMLElement = null) { constructor(html: string) {
this.e = element('div'); this.e = element('div');
this.a = anchor;
this.u(html); this.u(html);
} }
@ -302,6 +330,16 @@ export class HtmlTag {
this.n = Array.from(this.e.childNodes); this.n = Array.from(this.e.childNodes);
} }
l(nodes: any) {
this.n = this.n.map(n => {
if (n.nodeType === 3) {
return claim_text(nodes, (n as Text).data);
} else {
return claim_element(nodes, n.nodeName, n, n.namespaceURI !== 'http://www.w3.org/1999/xhtml');
}
});
}
p(html: string) { p(html: string) {
this.d(); this.d();
this.u(html); this.u(html);

@ -1,4 +1,4 @@
<title>Some Title</title>
<link href="/" rel="canonical">
<meta content="some description" name="description">
<meta content="some keywords" name="keywords"> <link rel="canonical" href="/"><meta name="description" content="some description"><meta name="keywords" content="some keywords"><title>Some Title</title>

@ -52,8 +52,9 @@ function create_each_block(ctx) {
t4 = text(t4_value); t4 = text(t4_value);
t5 = text(" ago:"); t5 = text(" ago:");
t6 = space(); t6 = space();
html_tag = new HtmlTag(raw_value);
attr(span, "class", "meta"); attr(span, "class", "meta");
html_tag = new HtmlTag(raw_value, null); html_tag.a = null;
attr(div, "class", "comment"); attr(div, "class", "comment");
}, },
m(target, anchor) { m(target, anchor) {

@ -28,10 +28,10 @@ function create_fragment(ctx) {
this.h(); this.h();
}, },
l(nodes) { l(nodes) {
img = claim_element(nodes, "IMG", { src: true, alt: true }); img = claim_element(nodes, "IMG", null);
t = claim_space(nodes); t = claim_space(nodes);
div = claim_element(nodes, "DIV", {}); div = claim_element(nodes, "DIV", null);
children(div).forEach(detach); children(div).children.forEach(detach);
this.h(); this.h();
}, },
h() { h() {

@ -28,9 +28,9 @@ function create_fragment(ctx) {
this.h(); this.h();
}, },
l(nodes) { l(nodes) {
img0 = claim_element(nodes, "IMG", { alt: true, src: true }); img0 = claim_element(nodes, "IMG", null);
t = claim_space(nodes); t = claim_space(nodes);
img1 = claim_element(nodes, "IMG", { alt: true, src: true }); img1 = claim_element(nodes, "IMG", null);
this.h(); this.h();
}, },
h() { h() {

Loading…
Cancel
Save