only recreate when the expression value changed

pull/5397/head
Tan Li Hau 5 years ago
parent a0f0e8ad67
commit e3112a4711

@ -30,12 +30,16 @@ export default class KeyBlockWrapper extends Wrapper {
this.dependencies = node.expression.dynamic_dependencies(); this.dependencies = node.expression.dynamic_dependencies();
this.block = block.child({ if (this.dependencies.length) {
comment: create_debugging_comment(node, renderer.component), block = block.child({
name: renderer.component.get_unique_name("create_key_block"), comment: create_debugging_comment(node, renderer.component),
type: "key" name: renderer.component.get_unique_name("create_key_block"),
}); type: "key"
});
renderer.blocks.push(block);
}
this.block = block;
this.fragment = new FragmentWrapper( this.fragment = new FragmentWrapper(
renderer, renderer,
this.block, this.block,
@ -44,11 +48,21 @@ export default class KeyBlockWrapper extends Wrapper {
strip_whitespace, strip_whitespace,
next_sibling next_sibling
); );
renderer.blocks.push(this.block);
} }
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { 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.fragment.render(
this.block, this.block,
null, null,
@ -60,6 +74,13 @@ export default class KeyBlockWrapper extends Wrapper {
); );
const dynamic = this.block.has_update_method; 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` block.chunks.init.push(b`
let ${this.var} = ${this.block.name}(#ctx); 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 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) { if (dynamic) {
const body = b` block.chunks.update.push(b`
${ if (${condition}) {
has_transitions ${body}
? b` } else {
@group_outros(); ${this.var}.p(#ctx, #dirty);
@transition_out(${this.var}, 1, 1, @noop);
@check_outros();
`
: b`${this.var}.d(1);`
} }
${this.var} = ${this.block.name}(#ctx); `);
${this.var}.c(); } else {
${has_transitions && b`@transition_in(${this.var})`} block.chunks.update.push(b`
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor}); if (${condition}) {
`; ${body}
}
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);`);
} }
if (has_transitions) { if (has_transitions) {

@ -0,0 +1,14 @@
// with reactive content beside `key`
export default {
html: `<div>00</div>`,
async test({ assert, component, target, window }) {
const div = target.querySelector('div');
component.reactive = 2;
assert.htmlEqual(target.innerHTML, `<div>02</div>`);
assert.strictEqual(div, target.querySelector('div'));
component.value = 5;
assert.htmlEqual(target.innerHTML, `<div>52</div>`);
assert.notStrictEqual(div, target.querySelector('div'));
}
};

@ -0,0 +1,8 @@
<script>
export let value = 0;
export let reactive = 0;
</script>
{#key value}
<div>{value}{reactive}</div>
{/key}

@ -0,0 +1,15 @@
export default {
html: `<div>1</div>`,
async test({ assert, component, target, window }) {
let div = target.querySelector("div");
await component.append(2);
assert.htmlEqual(target.innerHTML, `<div>1</div>`);
assert.strictEqual(div, target.querySelector("div"));
div = target.querySelector("div");
component.array = [3, 4];
assert.htmlEqual(target.innerHTML, `<div>3,4</div>`);
assert.notStrictEqual(div, target.querySelector("div"));
}
};

@ -0,0 +1,14 @@
<svelte:options immutable />
<script>
export let array = [1];
export function append(value) {
array.push(value);
array = array;
}
</script>
{#key array}
<div>{array.join(',')}</div>
{/key}

@ -0,0 +1,15 @@
export default {
html: `<div>1</div>`,
async test({ assert, component, target, window }) {
let div = target.querySelector("div");
await component.append(2);
assert.htmlEqual(target.innerHTML, `<div>1,2</div>`);
assert.notStrictEqual(div, target.querySelector("div"));
div = target.querySelector("div");
component.array = [3, 4];
assert.htmlEqual(target.innerHTML, `<div>3,4</div>`);
assert.notStrictEqual(div, target.querySelector("div"));
}
};

@ -0,0 +1,12 @@
<script>
export let array = [1];
export function append(value) {
array.push(value);
array = array;
}
</script>
{#key array}
<div>{array.join(',')}</div>
{/key}

@ -0,0 +1,18 @@
export default {
html: `<div>3</div>`,
async test({ assert, component, target, window }) {
const div = target.querySelector("div");
await component.mutate();
assert.htmlEqual(target.innerHTML, `<div>5</div>`);
assert.strictEqual(div, target.querySelector("div"));
await component.reassign();
assert.htmlEqual(target.innerHTML, `<div>7</div>`);
assert.strictEqual(div, target.querySelector("div"));
await component.changeKey();
assert.htmlEqual(target.innerHTML, `<div>7</div>`);
assert.notStrictEqual(div, target.querySelector("div"));
}
};

@ -0,0 +1,17 @@
<script>
let obj = { key: 1, value: 3 };
export function mutate() {
obj.value = 5;
}
export function reassign() {
obj = { key: 1, value: 7 };
}
export function changeKey() {
obj.key = 3;
}
</script>
{#key obj.key}
<div>{obj.value}</div>
{/key}

@ -0,0 +1,28 @@
export default {
html: `<div>000</div>`,
async test({ assert, component, target, window }) {
let div = target.querySelector("div");
component.value = 2;
assert.htmlEqual(target.innerHTML, `<div>200</div>`);
assert.notStrictEqual(div, target.querySelector("div"));
div = target.querySelector("div");
component.anotherValue = 5;
assert.htmlEqual(target.innerHTML, `<div>250</div>`);
assert.notStrictEqual(div, target.querySelector("div"));
div = target.querySelector("div");
component.thirdValue = 9;
assert.htmlEqual(target.innerHTML, `<div>259</div>`);
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, `<div>439</div>`);
assert.strictEqual(div, target.querySelector("div"));
}
};

@ -4,6 +4,6 @@
export let thirdValue = 0; export let thirdValue = 0;
</script> </script>
{#key [value, anotherValue]} {#key value + anotherValue}
<div>{value}{anotherValue}{thirdValue}</div> <div>{value}{anotherValue}{thirdValue}</div>
{/key} {/key}

@ -1,21 +0,0 @@
export default {
html: `<div>000</div>`,
async test({ assert, component, target, window }) {
let div = target.querySelector('div');
component.value = 2;
assert.htmlEqual(target.innerHTML, `<div>200</div>`);
assert.notStrictEqual(div, target.querySelector('div'));
div = target.querySelector('div');
component.anotherValue = 5;
assert.htmlEqual(target.innerHTML, `<div>250</div>`);
assert.notStrictEqual(div, target.querySelector('div'));
div = target.querySelector('div');
component.thirdValue = 9;
assert.htmlEqual(target.innerHTML, `<div>259</div>`);
assert.strictEqual(div, target.querySelector('div'));
}
};

@ -1,13 +1,17 @@
export default { export default {
html: `<div>00</div>`, html: `<div>0</div><div>0</div>`,
async test({ assert, component, target, window }) { async test({ assert, component, target, window }) {
const div = target.querySelector('div'); let [div1, div2] = target.querySelectorAll('div');
component.reactive = 2;
assert.htmlEqual(target.innerHTML, `<div>02</div>`);
assert.strictEqual(div, target.querySelector('div'));
component.value = 5; component.value = 5;
assert.htmlEqual(target.innerHTML, `<div>52</div>`); assert.htmlEqual(target.innerHTML, `<div>5</div><div>0</div>`);
assert.notStrictEqual(div, target.querySelector('div')); 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, `<div>5</div><div>10</div>`);
assert.strictEqual(div1, target.querySelectorAll('div')[0]);
assert.strictEqual(div2, target.querySelectorAll('div')[1]);
} }
}; };

@ -4,5 +4,7 @@
</script> </script>
{#key value} {#key value}
<div>{value}{reactive}</div> <div>{value}</div>
{/key} {/key}
<div>{reactive}</div>
Loading…
Cancel
Save