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}