diff --git a/src/compiler/compile/render_dom/wrappers/KeyBlock.ts b/src/compiler/compile/render_dom/wrappers/KeyBlock.ts
index 1f33b39196..359fb6946f 100644
--- a/src/compiler/compile/render_dom/wrappers/KeyBlock.ts
+++ b/src/compiler/compile/render_dom/wrappers/KeyBlock.ts
@@ -30,12 +30,16 @@ export default class KeyBlockWrapper extends Wrapper {
this.dependencies = node.expression.dynamic_dependencies();
- this.block = block.child({
- comment: create_debugging_comment(node, renderer.component),
- name: renderer.component.get_unique_name("create_key_block"),
- type: "key"
- });
+ if (this.dependencies.length) {
+ block = block.child({
+ comment: create_debugging_comment(node, renderer.component),
+ name: renderer.component.get_unique_name("create_key_block"),
+ type: "key"
+ });
+ renderer.blocks.push(block);
+ }
+ this.block = block;
this.fragment = new FragmentWrapper(
renderer,
this.block,
@@ -44,11 +48,21 @@ export default class KeyBlockWrapper extends Wrapper {
strip_whitespace,
next_sibling
);
-
- renderer.blocks.push(this.block);
}
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
+ if (this.dependencies.length === 0) {
+ this.render_static_key(block, parent_node, parent_nodes);
+ } else {
+ this.render_dynamic_key(block, parent_node, parent_nodes);
+ }
+ }
+
+ render_static_key(_block: Block, parent_node: Identifier, parent_nodes: Identifier) {
+ this.fragment.render(this.block, parent_node, parent_nodes);
+ }
+
+ render_dynamic_key(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
this.fragment.render(
this.block,
null,
@@ -60,6 +74,13 @@ export default class KeyBlockWrapper extends Wrapper {
);
const dynamic = this.block.has_update_method;
+ const previous_key = block.get_unique_name('previous_key');
+ const snippet = this.node.expression.manipulate(block);
+ block.add_variable(previous_key, snippet);
+
+ const not_equal = this.renderer.component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
+ const condition = x`${this.renderer.dirty(this.dependencies)} && ${not_equal}(${previous_key}, ${previous_key} = ${snippet})`;
+
block.chunks.init.push(b`
let ${this.var} = ${this.block.name}(#ctx);
`);
@@ -73,41 +94,36 @@ export default class KeyBlockWrapper extends Wrapper {
});`
);
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
+ const body = b`
+ ${
+ has_transitions
+ ? b`
+ @group_outros();
+ @transition_out(${this.var}, 1, 1, @noop);
+ @check_outros();
+ `
+ : b`${this.var}.d(1);`
+ }
+ ${this.var} = ${this.block.name}(#ctx);
+ ${this.var}.c();
+ ${has_transitions && b`@transition_in(${this.var})`}
+ ${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
+ `;
- if (this.dependencies.length) {
- const body = b`
- ${
- has_transitions
- ? b`
- @group_outros();
- @transition_out(${this.var}, 1, 1, @noop);
- @check_outros();
- `
- : b`${this.var}.d(1);`
+ if (dynamic) {
+ block.chunks.update.push(b`
+ if (${condition}) {
+ ${body}
+ } else {
+ ${this.var}.p(#ctx, #dirty);
}
- ${this.var} = ${this.block.name}(#ctx);
- ${this.var}.c();
- ${has_transitions && b`@transition_in(${this.var})`}
- ${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
- `;
-
- if (dynamic) {
- block.chunks.update.push(b`
- if (${this.renderer.dirty(this.dependencies)}) {
- ${body}
- } else {
- ${this.var}.p(#ctx, #dirty);
- }
- `);
- } else {
- block.chunks.update.push(b`
- if (${this.renderer.dirty(this.dependencies)}) {
- ${body}
- }
- `);
- }
- } else if (dynamic) {
- block.chunks.update.push(b`${this.var}.p(#ctx, #dirty);`);
+ `);
+ } else {
+ block.chunks.update.push(b`
+ if (${condition}) {
+ ${body}
+ }
+ `);
}
if (has_transitions) {
diff --git a/test/runtime/samples/key-block-2/_config.js b/test/runtime/samples/key-block-2/_config.js
new file mode 100644
index 0000000000..a7c53bd91f
--- /dev/null
+++ b/test/runtime/samples/key-block-2/_config.js
@@ -0,0 +1,14 @@
+// with reactive content beside `key`
+export default {
+ html: `
00
`,
+ async test({ assert, component, target, window }) {
+ const div = target.querySelector('div');
+ component.reactive = 2;
+ assert.htmlEqual(target.innerHTML, `02
`);
+ assert.strictEqual(div, target.querySelector('div'));
+
+ component.value = 5;
+ assert.htmlEqual(target.innerHTML, `52
`);
+ assert.notStrictEqual(div, target.querySelector('div'));
+ }
+};
diff --git a/test/runtime/samples/key-block-2/main.svelte b/test/runtime/samples/key-block-2/main.svelte
new file mode 100644
index 0000000000..466d20b10a
--- /dev/null
+++ b/test/runtime/samples/key-block-2/main.svelte
@@ -0,0 +1,8 @@
+
+
+{#key value}
+ {value}{reactive}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-array-immutable/_config.js b/test/runtime/samples/key-block-array-immutable/_config.js
new file mode 100644
index 0000000000..fb94556c0f
--- /dev/null
+++ b/test/runtime/samples/key-block-array-immutable/_config.js
@@ -0,0 +1,15 @@
+export default {
+ html: `1
`,
+ async test({ assert, component, target, window }) {
+ let div = target.querySelector("div");
+ await component.append(2);
+ assert.htmlEqual(target.innerHTML, `1
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.array = [3, 4];
+ assert.htmlEqual(target.innerHTML, `3,4
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-array-immutable/main.svelte b/test/runtime/samples/key-block-array-immutable/main.svelte
new file mode 100644
index 0000000000..e666275af4
--- /dev/null
+++ b/test/runtime/samples/key-block-array-immutable/main.svelte
@@ -0,0 +1,14 @@
+
+
+
+
+{#key array}
+ {array.join(',')}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-array/_config.js b/test/runtime/samples/key-block-array/_config.js
new file mode 100644
index 0000000000..05d5fe9995
--- /dev/null
+++ b/test/runtime/samples/key-block-array/_config.js
@@ -0,0 +1,15 @@
+export default {
+ html: `1
`,
+ async test({ assert, component, target, window }) {
+ let div = target.querySelector("div");
+ await component.append(2);
+ assert.htmlEqual(target.innerHTML, `1,2
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.array = [3, 4];
+ assert.htmlEqual(target.innerHTML, `3,4
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-array/main.svelte b/test/runtime/samples/key-block-array/main.svelte
new file mode 100644
index 0000000000..5a4054b043
--- /dev/null
+++ b/test/runtime/samples/key-block-array/main.svelte
@@ -0,0 +1,12 @@
+
+
+{#key array}
+ {array.join(',')}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-expression-2/_config.js b/test/runtime/samples/key-block-expression-2/_config.js
new file mode 100644
index 0000000000..236c72fa3d
--- /dev/null
+++ b/test/runtime/samples/key-block-expression-2/_config.js
@@ -0,0 +1,18 @@
+export default {
+ html: `3
`,
+ async test({ assert, component, target, window }) {
+ const div = target.querySelector("div");
+
+ await component.mutate();
+ assert.htmlEqual(target.innerHTML, `5
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ await component.reassign();
+ assert.htmlEqual(target.innerHTML, `7
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ await component.changeKey();
+ assert.htmlEqual(target.innerHTML, `7
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-expression-2/main.svelte b/test/runtime/samples/key-block-expression-2/main.svelte
new file mode 100644
index 0000000000..5525f63761
--- /dev/null
+++ b/test/runtime/samples/key-block-expression-2/main.svelte
@@ -0,0 +1,17 @@
+
+
+{#key obj.key}
+ {obj.value}
+{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-expression/_config.js b/test/runtime/samples/key-block-expression/_config.js
new file mode 100644
index 0000000000..78890988ea
--- /dev/null
+++ b/test/runtime/samples/key-block-expression/_config.js
@@ -0,0 +1,28 @@
+export default {
+ html: `000
`,
+ async test({ assert, component, target, window }) {
+ let div = target.querySelector("div");
+ component.value = 2;
+ assert.htmlEqual(target.innerHTML, `200
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.anotherValue = 5;
+ assert.htmlEqual(target.innerHTML, `250
`);
+ assert.notStrictEqual(div, target.querySelector("div"));
+
+ div = target.querySelector("div");
+
+ component.thirdValue = 9;
+ assert.htmlEqual(target.innerHTML, `259
`);
+ assert.strictEqual(div, target.querySelector("div"));
+
+ // make dirty while maintain the value of `value + anotherValue`
+ // should update the content, but not recreate the elements
+ await component.$set({ value: 4, anotherValue: 3 });
+
+ assert.htmlEqual(target.innerHTML, `439
`);
+ assert.strictEqual(div, target.querySelector("div"));
+ }
+};
diff --git a/test/runtime/samples/key-block-multiple/main.svelte b/test/runtime/samples/key-block-expression/main.svelte
similarity index 80%
rename from test/runtime/samples/key-block-multiple/main.svelte
rename to test/runtime/samples/key-block-expression/main.svelte
index 88c9213506..dd752e8b8f 100644
--- a/test/runtime/samples/key-block-multiple/main.svelte
+++ b/test/runtime/samples/key-block-expression/main.svelte
@@ -4,6 +4,6 @@
export let thirdValue = 0;
-{#key [value, anotherValue]}
+{#key value + anotherValue}
{value}{anotherValue}{thirdValue}
{/key}
\ No newline at end of file
diff --git a/test/runtime/samples/key-block-multiple/_config.js b/test/runtime/samples/key-block-multiple/_config.js
deleted file mode 100644
index 7a8314d8ff..0000000000
--- a/test/runtime/samples/key-block-multiple/_config.js
+++ /dev/null
@@ -1,21 +0,0 @@
-export default {
- html: `000
`,
- async test({ assert, component, target, window }) {
- let div = target.querySelector('div');
- component.value = 2;
- assert.htmlEqual(target.innerHTML, `200
`);
- assert.notStrictEqual(div, target.querySelector('div'));
-
- div = target.querySelector('div');
-
- component.anotherValue = 5;
- assert.htmlEqual(target.innerHTML, `250
`);
- assert.notStrictEqual(div, target.querySelector('div'));
-
- div = target.querySelector('div');
-
- component.thirdValue = 9;
- assert.htmlEqual(target.innerHTML, `259
`);
- assert.strictEqual(div, target.querySelector('div'));
- }
-};
diff --git a/test/runtime/samples/key-block/_config.js b/test/runtime/samples/key-block/_config.js
index 8524117c8d..ad206c3b06 100644
--- a/test/runtime/samples/key-block/_config.js
+++ b/test/runtime/samples/key-block/_config.js
@@ -1,13 +1,17 @@
export default {
- html: `00
`,
+ html: `0
0
`,
async test({ assert, component, target, window }) {
- const div = target.querySelector('div');
- component.reactive = 2;
- assert.htmlEqual(target.innerHTML, `02
`);
- assert.strictEqual(div, target.querySelector('div'));
+ let [div1, div2] = target.querySelectorAll('div');
component.value = 5;
- assert.htmlEqual(target.innerHTML, `52
`);
- assert.notStrictEqual(div, target.querySelector('div'));
+ assert.htmlEqual(target.innerHTML, `5
0
`);
+ assert.notStrictEqual(div1, target.querySelectorAll('div')[0]);
+ assert.strictEqual(div2, target.querySelectorAll('div')[1]);
+ [div1, div2] = target.querySelectorAll('div');
+
+ component.reactive = 10;
+ assert.htmlEqual(target.innerHTML, `5
10
`);
+ assert.strictEqual(div1, target.querySelectorAll('div')[0]);
+ assert.strictEqual(div2, target.querySelectorAll('div')[1]);
}
};
diff --git a/test/runtime/samples/key-block/main.svelte b/test/runtime/samples/key-block/main.svelte
index 466d20b10a..ac3c340770 100644
--- a/test/runtime/samples/key-block/main.svelte
+++ b/test/runtime/samples/key-block/main.svelte
@@ -4,5 +4,7 @@
{#key value}
- {value}{reactive}
-{/key}
\ No newline at end of file
+ {value}
+{/key}
+
+{reactive}
\ No newline at end of file