diff --git a/.changeset/khaki-jobs-smoke.md b/.changeset/khaki-jobs-smoke.md new file mode 100644 index 000000000000..d72bbab7a0f7 --- /dev/null +++ b/.changeset/khaki-jobs-smoke.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: optimize non-reactive if statements diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index de118960ecea..f8d9babecc04 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -61,7 +61,10 @@ function open(parser) { end: -1, test: read_expression(parser), consequent: create_fragment(), - alternate: null + alternate: null, + metadata: { + expression: create_expression_metadata() + } }); parser.allow_whitespace(); @@ -363,7 +366,10 @@ function next(parser) { elseif: true, test: expression, consequent: create_fragment(), - alternate: null + alternate: null, + metadata: { + expression: create_expression_metadata() + } }); parser.stack.push(child); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js index a65771bcfca9..dcdae3587f63 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js @@ -17,5 +17,11 @@ export function IfBlock(node, context) { mark_subtree_dynamic(context.path); - context.next(); + context.visit(node.test, { + ...context.state, + expression: node.metadata.expression + }); + + context.visit(node.consequent); + if (node.alternate) context.visit(node.alternate); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js index 6f8894c2c83b..c8749cde71ed 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js @@ -8,9 +8,24 @@ import * as b from '../../../../utils/builders.js'; * @param {ComponentContext} context */ export function IfBlock(node, context) { + const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent)); + const alternate = node.alternate && /** @type {BlockStatement} */ (context.visit(node.alternate)); + context.state.template.push_quasi(''); - const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent)); + // compile to if rather than $.if for non-reactive if statements + // NOTE: doesn't handle else/elseif yet or mismatches. also, this probably breaks transition locality + if (!node.metadata.expression.has_state && !node.elseif) { + context.state.init.push( + b.block([ + b.let(b.id('$$anchor'), context.state.node), + b.stmt(b.call('$.next')), + b.if(node.test, consequent, alternate ?? undefined) + ]) + ); + + return; + } const args = [ context.state.node, @@ -18,12 +33,8 @@ export function IfBlock(node, context) { b.arrow([b.id('$$anchor')], consequent) ]; - if (node.alternate || node.elseif) { - args.push( - node.alternate - ? b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.alternate))) - : b.literal(null) - ); + if (alternate || node.elseif) { + args.push(alternate ? b.arrow([b.id('$$anchor')], alternate) : b.literal(null)); } if (node.elseif) { diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 9da0320432d4..c4753f791267 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -410,6 +410,10 @@ export namespace AST { test: Expression; consequent: Fragment; alternate: Fragment | null; + /** @internal */ + metadata: { + expression: ExpressionMetadata; + }; } /** An `{#await ...}` block */ diff --git a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js index 6a9027e41fbb..9dad39d81e9e 100644 --- a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js @@ -7,4 +7,4 @@ const __DECLARED_ASSET_3__ = "__VITE_ASSET__2AM7_y_g__"; export default function Inline_module_vars($$payload) { $$payload.out += ` `; -} +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/_expected/client/index.svelte.js new file mode 100644 index 000000000000..db544ff074a0 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/_expected/client/index.svelte.js @@ -0,0 +1,51 @@ +import "svelte/internal/disclose-version"; +import * as $ from "svelte/internal/client"; + +var root = $.template(` `, 1); + +export default function Non_reactive_control_structures($$anchor) { + const a = true; + let b = true; + var fragment = root(); + var node = $.first_child(fragment); + + { + let $$anchor = node; + + $.next(); + + if (a) { + var text = $.text("hello"); + + $.append($$anchor, text); + } + } + + var node_1 = $.sibling(node, 2); + + $.if(node_1, () => b, ($$anchor) => { + var text_1 = $.text("world"); + + $.append($$anchor, text_1); + }); + + var node_2 = $.sibling(node_1, 2); + + { + let $$anchor = node_2; + + $.next(); + + if (a) { + var text_2 = $.text("hi"); + + $.append($$anchor, text_2); + } else { + var text_3 = $.text("earth"); + + $.append($$anchor, text_3); + } + } + + $.append($$anchor, fragment); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/_expected/server/index.svelte.js new file mode 100644 index 000000000000..acb8c7ef05d5 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/_expected/server/index.svelte.js @@ -0,0 +1,34 @@ +import * as $ from "svelte/internal/server"; + +export default function Non_reactive_control_structures($$payload) { + const a = true; + let b = true; + + if (a) { + $$payload.out += ""; + $$payload.out += `hello`; + } else { + $$payload.out += ""; + } + + $$payload.out += ` `; + + if (b) { + $$payload.out += ""; + $$payload.out += `world`; + } else { + $$payload.out += ""; + } + + $$payload.out += ` `; + + if (a) { + $$payload.out += ""; + $$payload.out += `hi`; + } else { + $$payload.out += ""; + $$payload.out += `earth`; + } + + $$payload.out += ``; +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/index.svelte b/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/index.svelte new file mode 100644 index 000000000000..a55acc736e60 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/non-reactive-control-structures/index.svelte @@ -0,0 +1,20 @@ + + + + +{#if a} + hello +{/if} + +{#if b} + world +{/if} + +{#if a} + hi +{:else} + earth +{/if}