use maps for keyed each block lookups - fixes #2430

pull/2433/head
Richard Harris 6 years ago
parent e7f54679b5
commit c988457afa

@ -294,7 +294,7 @@ export default class EachBlockWrapper extends Wrapper {
const lookup = block.get_unique_name(`${this.var}_lookup`); const lookup = block.get_unique_name(`${this.var}_lookup`);
block.add_variable(iterations, '[]'); block.add_variable(iterations, '[]');
block.add_variable(lookup, `@blank_object()`); block.add_variable(lookup, `new Map()`);
if (this.fragment.nodes[0].is_dom_node()) { if (this.fragment.nodes[0].is_dom_node()) {
this.block.first = this.fragment.nodes[0].var; 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) { 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 child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i);
let key = ${get_key}(child_ctx); 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));
} }
`); `);

@ -2,7 +2,7 @@ import { on_outro } from './transitions.js';
export function destroy_block(block, lookup) { export function destroy_block(block, lookup) {
block.d(1); block.d(1);
lookup[block.key] = null; lookup.delete(block.key);
} }
export function outro_and_destroy_block(block, lookup) { 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; while (i--) old_indexes[old_blocks[i].key] = i;
const new_blocks = []; const new_blocks = [];
const new_lookup = {}; const new_lookup = new Map();
const deltas = {}; const deltas = new Map();
i = n; i = n;
while (i--) { while (i--) {
const child_ctx = get_context(ctx, list, i); const child_ctx = get_context(ctx, list, i);
const key = get_key(child_ctx); const key = get_key(child_ctx);
let block = lookup[key]; let block = lookup.get(key);
if (!block) { if (!block) {
block = create_each_block(key, child_ctx); 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); 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 will_move = new Set();
const did_move = {}; const did_move = new Set();
function insert(block) { function insert(block) {
if (block.i) block.i(1); if (block.i) block.i(1);
block.m(node, next); block.m(node, next);
lookup[block.key] = block; lookup.set(block.key, block);
next = block.first; next = block.first;
n--; n--;
} }
@ -72,32 +72,32 @@ export function update_keyed_each(old_blocks, changed, get_key, dynamic, ctx, li
n--; n--;
} }
else if (!new_lookup[old_key]) { else if (!new_lookup.has(old_key)) {
// remove old block // remove old block
destroy(old_block, lookup); destroy(old_block, lookup);
o--; o--;
} }
else if (!lookup[new_key] || will_move[new_key]) { else if (!lookup.has(new_key) || will_move.has(new_key)) {
insert(new_block); insert(new_block);
} }
else if (did_move[old_key]) { else if (did_move.has(old_key)) {
o--; o--;
} else if (deltas[new_key] > deltas[old_key]) { } else if (deltas.get(new_key) > deltas.get(old_key)) {
did_move[new_key] = true; did_move.add(new_key);
insert(new_block); insert(new_block);
} else { } else {
will_move[old_key] = true; will_move.add(old_key);
o--; o--;
} }
} }
while (o--) { while (o--) {
const old_block = old_blocks[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]); while (n) insert(new_blocks[n - 1]);

@ -2,7 +2,6 @@
import { import {
SvelteComponent, SvelteComponent,
append, append,
blank_object,
create_animation, create_animation,
detach, detach,
element, element,
@ -73,7 +72,7 @@ function create_each_block(key_1, ctx) {
} }
function create_fragment(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; var each_value = ctx.things;
@ -82,7 +81,7 @@ function create_fragment(ctx) {
for (var i = 0; i < each_value.length; i += 1) { for (var i = 0; i < each_value.length; i += 1) {
let child_ctx = get_each_context(ctx, each_value, i); let child_ctx = get_each_context(ctx, each_value, i);
let key = get_key(child_ctx); 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 { return {

@ -2,7 +2,6 @@
import { import {
SvelteComponent, SvelteComponent,
append, append,
blank_object,
destroy_block, destroy_block,
detach, detach,
element, element,
@ -57,7 +56,7 @@ function create_each_block(key_1, ctx) {
} }
function create_fragment(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; var each_value = ctx.things;
@ -66,7 +65,7 @@ function create_fragment(ctx) {
for (var i = 0; i < each_value.length; i += 1) { for (var i = 0; i < each_value.length; i += 1) {
let child_ctx = get_each_context(ctx, each_value, i); let child_ctx = get_each_context(ctx, each_value, i);
let key = get_key(child_ctx); 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 { return {

@ -0,0 +1,25 @@
export default {
props: {
todos: [
{ description: 'implement keyed each blocks' },
{ description: 'implement client-side hydration' }
]
},
html: `
<p>1: implement keyed each blocks</p>
<p>2: implement client-side hydration</p>
`,
test({ assert, component, target }) {
const [p1, p2] = target.querySelectorAll('p');
component.todos = [component.todos[1]];
assert.htmlEqual(target.innerHTML, '<p>1: implement client-side hydration</p>');
const [p3] = target.querySelectorAll('p');
assert.ok(!target.contains(p1), 'first <p> element should be removed');
assert.equal(p2, p3, 'second <p> element should be retained');
}
};

@ -0,0 +1,7 @@
<script>
export let todos;
</script>
{#each todos as todo, i (todo)}
<p>{i+1}: {todo.description}</p>
{/each}
Loading…
Cancel
Save