Skip to content

Commit

Permalink
feat: optimise svelte-element output code for static tag and static a…
Browse files Browse the repository at this point in the history
…ttribute
  • Loading branch information
tanhauhau committed Jan 2, 2023
1 parent e1a1c7f commit 10b2abd
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 182 deletions.
166 changes: 99 additions & 67 deletions src/compiler/compile/render_dom/wrappers/Element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export default class ElementWrapper extends Wrapper {
bindings: Binding[];
event_handlers: EventHandler[];
class_dependencies: string[];
has_dynamic_attribute: boolean;

select_binding_dependencies?: Set<string>;

Expand Down Expand Up @@ -219,6 +220,7 @@ export default class ElementWrapper extends Wrapper {
}
return new AttributeWrapper(this, block, attribute);
});
this.has_dynamic_attribute = !!this.attributes.find(attr => attr.node.get_dependencies().length > 0);

// ordinarily, there'll only be one... but we need to handle
// the rare case where an element can have multiple bindings,
Expand Down Expand Up @@ -287,9 +289,8 @@ export default class ElementWrapper extends Wrapper {
(x`#nodes` as unknown) as Identifier
);

const previous_tag = block.get_unique_name('previous_tag');
const is_tag_dynamic = this.node.tag_expr.dynamic_dependencies().length > 0;
const tag = this.node.tag_expr.manipulate(block);
block.add_variable(previous_tag, tag);

block.chunks.init.push(b`
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
Expand All @@ -311,44 +312,54 @@ export default class ElementWrapper extends Wrapper {
if (${this.var}) ${this.var}.m(${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});
`);

const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const has_transitions = !!(this.node.intro || this.node.outro);
const not_equal = this.renderer.component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
if (is_tag_dynamic) {
const previous_tag = block.get_unique_name('previous_tag');
block.add_variable(previous_tag, tag);
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const has_transitions = !!(this.node.intro || this.node.outro);
const not_equal = this.renderer.component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;

block.chunks.update.push(b`
if (${tag}) {
if (!${previous_tag}) {
${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${this.var}.c();
${has_transitions && b`@transition_in(${this.var})`}
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else if (${not_equal}(${previous_tag}, ${tag})) {
${this.var}.d(1);
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && this.node.children.length > 0 && b`@validate_void_dynamic_element(${tag});`}
${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${this.var}.c();
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else {
block.chunks.update.push(b`
if (${tag}) {
if (!${previous_tag}) {
${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${this.var}.c();
${has_transitions && b`@transition_in(${this.var})`}
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else if (${not_equal}(${previous_tag}, ${tag})) {
${this.var}.d(1);
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && this.node.children.length > 0 && b`@validate_void_dynamic_element(${tag});`}
${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${this.var}.c();
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else {
${this.var}.p(#ctx, #dirty);
}
} else if (${previous_tag}) {
${has_transitions
? b`
@group_outros();
@transition_out(${this.var}, 1, 1, () => {
${this.var} = null;
});
@check_outros();
`
: b`
${this.var}.d(1);
${this.var} = null;
`
}
}
${previous_tag} = ${tag};
`);
} else {
block.chunks.update.push(b`
if (${tag}) {
${this.var}.p(#ctx, #dirty);
}
} else if (${previous_tag}) {
${has_transitions
? b`
@group_outros();
@transition_out(${this.var}, 1, 1, () => {
${this.var} = null;
});
@check_outros();
`
: b`
${this.var}.d(1);
${this.var} = null;
`
}
}
${previous_tag} = ${tag};
`);
`);
}

if (this.child_dynamic_element_block.has_intros) {
block.chunks.intro.push(b`@transition_in(${this.var});`);
Expand Down Expand Up @@ -487,7 +498,11 @@ export default class ElementWrapper extends Wrapper {
block.maintain_context = true;
}

this.add_attributes(block);
if (this.node.is_dynamic_element) {
this.add_dynamic_element_attributes(block);
} else {
this.add_attributes(block);
}
this.add_directives_in_order(block);
this.add_transitions(block);
this.add_animation(block);
Expand Down Expand Up @@ -765,7 +780,7 @@ export default class ElementWrapper extends Wrapper {
}
});

if (this.node.attributes.some(attr => attr.is_spread) || this.node.is_dynamic_element) {
if (this.node.attributes.some(attr => attr.is_spread)) {
this.add_spread_attributes(block);
return;
}
Expand Down Expand Up @@ -814,35 +829,22 @@ export default class ElementWrapper extends Wrapper {
}
`);

const fn = this.node.namespace === namespaces.svg ? x`@set_svg_attributes` : x`@set_attributes`;
const fn =
this.node.namespace === namespaces.svg
? x`@set_svg_attributes`
: this.node.is_dynamic_element
? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})`
: x`@set_attributes`;

if (this.node.is_dynamic_element) {
// call attribute bindings for custom element if tag is custom element
const tag = this.node.tag_expr.manipulate(block);
const attr_update = this.node.namespace === namespaces.svg
? b`${fn}(${this.var}, ${data});`
: b`
if (/-/.test(${tag})) {
@set_custom_element_data_map(${this.var}, ${data});
} else {
${fn}(${this.var}, ${data});
}`;
block.chunks.hydrate.push(attr_update);
block.chunks.update.push(b`
${data} = @get_spread_update(${levels}, [${updates}]);
${attr_update}`
);
} else {
block.chunks.hydrate.push(
b`${fn}(${this.var}, ${data});`
);
block.chunks.hydrate.push(
b`${fn}(${this.var}, ${data});`
);

block.chunks.update.push(b`
${fn}(${this.var}, ${data} = @get_spread_update(${levels}, [
${updates}
]));
`);
}
block.chunks.update.push(b`
${fn}(${this.var}, ${data} = @get_spread_update(${levels}, [
${updates}
]));
`);

// handle edge cases for elements
if (this.node.name === 'select') {
Expand Down Expand Up @@ -880,6 +882,36 @@ export default class ElementWrapper extends Wrapper {
}
}

add_dynamic_element_attributes(block: Block) {
if (this.attributes.length === 0) return;

if (this.has_dynamic_attribute) {
// has dynamic attribute
this.add_spread_attributes(block);
return;
}

const static_attributes = [];
this.attributes.forEach((attr) => {
if (attr instanceof SpreadAttributeWrapper) {
static_attributes.push({ type: 'SpreadElement', argument: attr.node.expression.node });
} else {
const name = attr.property_name || attr.name;
static_attributes.push(p`${name}: ${attr.get_value(block)}`);
}
});
const fn =
this.node.namespace === namespaces.svg
? x`@set_svg_attributes`
: this.node.is_dynamic_element
? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})`
: x`@set_attributes`;

block.chunks.hydrate.push(
b`${fn}(${this.var}, {${static_attributes}});`
);
}

add_transitions(block: Block) {
const { intro, outro } = this.node;
if (!intro && !outro) return;
Expand Down Expand Up @@ -1077,7 +1109,7 @@ export default class ElementWrapper extends Wrapper {

block.chunks.hydrate.push(updater);

if (has_spread || this.node.is_dynamic_element) {
if (this.node.is_dynamic_element ? this.has_dynamic_attribute : has_spread) {
block.chunks.update.push(updater);
} else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies);
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/internal/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ export function set_custom_element_data(node, prop, value) {
}
}

export function set_dynamic_element_data(tag: string) {
return function (node: HTMLElement, data_map: { [x: string]: string }) {
if (/-/.test(tag)) set_custom_element_data_map(node, data_map);
else set_attributes(node, data_map);
};
}

export function xlink_attr(node, attribute, value) {
node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);
}
Expand Down
74 changes: 5 additions & 69 deletions test/js/samples/svelte-element-event-handlers/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,30 @@
import {
SvelteComponent,
append,
assign,
bubble,
detach,
element,
empty,
get_spread_update,
init,
insert,
listen,
noop,
run_all,
safe_not_equal,
set_attributes,
set_custom_element_data_map
set_dynamic_element_data
} from "svelte/internal";

function create_dynamic_element(ctx) {
let svelte_element1;
let svelte_element0;
let mounted;
let dispose;
let svelte_element0_levels = [{ class: "inner" }];
let svelte_element0_data = {};

for (let i = 0; i < svelte_element0_levels.length; i += 1) {
svelte_element0_data = assign(svelte_element0_data, svelte_element0_levels[i]);
}

let svelte_element1_levels = [{ class: "outer" }];
let svelte_element1_data = {};

for (let i = 0; i < svelte_element1_levels.length; i += 1) {
svelte_element1_data = assign(svelte_element1_data, svelte_element1_levels[i]);
}

return {
c() {
svelte_element1 = element(a);
svelte_element0 = element(span);

if ((/-/).test(span)) {
set_custom_element_data_map(svelte_element0, svelte_element0_data);
} else {
set_attributes(svelte_element0, svelte_element0_data);
}

if ((/-/).test(a)) {
set_custom_element_data_map(svelte_element1, svelte_element1_data);
} else {
set_attributes(svelte_element1, svelte_element1_data);
}
set_dynamic_element_data(span)(svelte_element0, { class: "inner" });
set_dynamic_element_data(a)(svelte_element1, { class: "outer" });
},
m(target, anchor) {
insert(target, svelte_element1, anchor);
Expand All @@ -69,23 +42,7 @@ function create_dynamic_element(ctx) {
mounted = true;
}
},
p(ctx, dirty) {
svelte_element0_data = get_spread_update(svelte_element0_levels, [{ class: "inner" }]);

if ((/-/).test(span)) {
set_custom_element_data_map(svelte_element0, svelte_element0_data);
} else {
set_attributes(svelte_element0, svelte_element0_data);
}

svelte_element1_data = get_spread_update(svelte_element1_levels, [{ class: "outer" }]);

if ((/-/).test(a)) {
set_custom_element_data_map(svelte_element1, svelte_element1_data);
} else {
set_attributes(svelte_element1, svelte_element1_data);
}
},
p: noop,
d(detaching) {
if (detaching) detach(svelte_element1);
mounted = false;
Expand All @@ -95,44 +52,23 @@ function create_dynamic_element(ctx) {
}

function create_fragment(ctx) {
let previous_tag = a;
let svelte_element_anchor;
let svelte_element = a && create_dynamic_element(ctx);

return {
c() {
if (svelte_element) svelte_element.c();
svelte_element_anchor = empty();
},
m(target, anchor) {
if (svelte_element) svelte_element.m(target, anchor);
insert(target, svelte_element_anchor, anchor);
},
p(ctx, [dirty]) {
if (a) {
if (!previous_tag) {
svelte_element = create_dynamic_element(ctx);
svelte_element.c();
svelte_element.m(svelte_element_anchor.parentNode, svelte_element_anchor);
} else if (safe_not_equal(previous_tag, a)) {
svelte_element.d(1);
svelte_element = create_dynamic_element(ctx);
svelte_element.c();
svelte_element.m(svelte_element_anchor.parentNode, svelte_element_anchor);
} else {
svelte_element.p(ctx, dirty);
}
} else if (previous_tag) {
svelte_element.d(1);
svelte_element = null;
svelte_element.p(ctx, dirty);
}

previous_tag = a;
},
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(svelte_element_anchor);
if (svelte_element) svelte_element.d(detaching);
}
};
Expand Down
Loading

0 comments on commit 10b2abd

Please sign in to comment.