diff --git a/src/compile/render-dom/wrappers/EachBlock.ts b/src/compile/render-dom/wrappers/EachBlock.ts index 419543e018..050107a573 100644 --- a/src/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compile/render-dom/wrappers/EachBlock.ts @@ -294,7 +294,7 @@ export default class EachBlockWrapper extends Wrapper { const lookup = block.get_unique_name(`${this.var}_lookup`); block.add_variable(iterations, '[]'); - block.add_variable(lookup, `@blank_object()`); + block.add_variable(lookup, `new Map()`); if (this.fragment.nodes[0].is_dom_node()) { this.block.first = this.fragment.nodes[0].var; @@ -314,7 +314,7 @@ export default class EachBlockWrapper extends Wrapper { for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) { let child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i); let key = ${get_key}(child_ctx); - ${iterations}[#i] = ${lookup}[key] = ${create_each_block}(key, child_ctx); + ${lookup}.set(key, ${iterations}[#i] = ${create_each_block}(key, child_ctx)); } `); diff --git a/src/internal/keyed-each.js b/src/internal/keyed-each.js index 307a6b0dbc..c40a1cc00b 100644 --- a/src/internal/keyed-each.js +++ b/src/internal/keyed-each.js @@ -2,7 +2,7 @@ import { on_outro } from './transitions.js'; export function destroy_block(block, lookup) { block.d(1); - lookup[block.key] = null; + lookup.delete(block.key); } export function outro_and_destroy_block(block, lookup) { @@ -27,14 +27,14 @@ export function update_keyed_each(old_blocks, changed, get_key, dynamic, ctx, li while (i--) old_indexes[old_blocks[i].key] = i; const new_blocks = []; - const new_lookup = {}; - const deltas = {}; + const new_lookup = new Map(); + const deltas = new Map(); i = n; while (i--) { const child_ctx = get_context(ctx, list, i); const key = get_key(child_ctx); - let block = lookup[key]; + let block = lookup.get(key); if (!block) { block = create_each_block(key, child_ctx); @@ -43,18 +43,18 @@ export function update_keyed_each(old_blocks, changed, get_key, dynamic, ctx, li block.p(changed, child_ctx); } - new_blocks[i] = new_lookup[key] = block; + new_lookup.set(key, new_blocks[i] = block); - if (key in old_indexes) deltas[key] = Math.abs(i - old_indexes[key]); + if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key])); } - const will_move = {}; - const did_move = {}; + const will_move = new Set(); + const did_move = new Set(); function insert(block) { if (block.i) block.i(1); block.m(node, next); - lookup[block.key] = block; + lookup.set(block.key, block); next = block.first; n--; } @@ -72,32 +72,32 @@ export function update_keyed_each(old_blocks, changed, get_key, dynamic, ctx, li n--; } - else if (!new_lookup[old_key]) { + else if (!new_lookup.has(old_key)) { // remove old block destroy(old_block, lookup); o--; } - else if (!lookup[new_key] || will_move[new_key]) { + else if (!lookup.has(new_key) || will_move.has(new_key)) { insert(new_block); } - else if (did_move[old_key]) { + else if (did_move.has(old_key)) { o--; - } else if (deltas[new_key] > deltas[old_key]) { - did_move[new_key] = true; + } else if (deltas.get(new_key) > deltas.get(old_key)) { + did_move.add(new_key); insert(new_block); } else { - will_move[old_key] = true; + will_move.add(old_key); o--; } } while (o--) { const old_block = old_blocks[o]; - if (!new_lookup[old_block.key]) destroy(old_block, lookup); + if (!new_lookup.has(old_block.key)) destroy(old_block, lookup); } while (n) insert(new_blocks[n - 1]); diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js index b168fb024e..d18c569fa1 100644 --- a/test/js/samples/each-block-keyed-animated/expected.js +++ b/test/js/samples/each-block-keyed-animated/expected.js @@ -2,7 +2,6 @@ import { SvelteComponent, append, - blank_object, create_animation, detach, element, @@ -73,7 +72,7 @@ function create_each_block(key_1, ctx) { } function create_fragment(ctx) { - var each_blocks = [], each_1_lookup = blank_object(), each_1_anchor; + var each_blocks = [], each_1_lookup = new Map(), each_1_anchor; var each_value = ctx.things; @@ -82,7 +81,7 @@ function create_fragment(ctx) { for (var i = 0; i < each_value.length; i += 1) { let child_ctx = get_each_context(ctx, each_value, i); let key = get_key(child_ctx); - each_blocks[i] = each_1_lookup[key] = create_each_block(key, child_ctx); + each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx)); } return { diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js index a24841813c..efb58ebf6a 100644 --- a/test/js/samples/each-block-keyed/expected.js +++ b/test/js/samples/each-block-keyed/expected.js @@ -2,7 +2,6 @@ import { SvelteComponent, append, - blank_object, destroy_block, detach, element, @@ -57,7 +56,7 @@ function create_each_block(key_1, ctx) { } function create_fragment(ctx) { - var each_blocks = [], each_1_lookup = blank_object(), each_1_anchor; + var each_blocks = [], each_1_lookup = new Map(), each_1_anchor; var each_value = ctx.things; @@ -66,7 +65,7 @@ function create_fragment(ctx) { for (var i = 0; i < each_value.length; i += 1) { let child_ctx = get_each_context(ctx, each_value, i); let key = get_key(child_ctx); - each_blocks[i] = each_1_lookup[key] = create_each_block(key, child_ctx); + each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx)); } return { diff --git a/test/runtime/samples/each-block-keyed-object-identity/_config.js b/test/runtime/samples/each-block-keyed-object-identity/_config.js new file mode 100644 index 0000000000..57f5422274 --- /dev/null +++ b/test/runtime/samples/each-block-keyed-object-identity/_config.js @@ -0,0 +1,25 @@ +export default { + props: { + todos: [ + { description: 'implement keyed each blocks' }, + { description: 'implement client-side hydration' } + ] + }, + + html: ` +

1: implement keyed each blocks

+

2: implement client-side hydration

+ `, + + test({ assert, component, target }) { + const [p1, p2] = target.querySelectorAll('p'); + + component.todos = [component.todos[1]]; + assert.htmlEqual(target.innerHTML, '

1: implement client-side hydration

'); + + const [p3] = target.querySelectorAll('p'); + + assert.ok(!target.contains(p1), 'first

element should be removed'); + assert.equal(p2, p3, 'second

element should be retained'); + } +}; diff --git a/test/runtime/samples/each-block-keyed-object-identity/main.svelte b/test/runtime/samples/each-block-keyed-object-identity/main.svelte new file mode 100644 index 0000000000..3a1a46d7ec --- /dev/null +++ b/test/runtime/samples/each-block-keyed-object-identity/main.svelte @@ -0,0 +1,7 @@ + + +{#each todos as todo, i (todo)} +

{i+1}: {todo.description}

+{/each}