[fix] allow invalidating variables from @const declared function (#7858)

pull/8087/head
Tan Li Hau 3 years ago committed by GitHub
parent 5eb1ff9946
commit 2f61907ec1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -274,41 +274,7 @@ export default class Expression {
);
const declaration = b`const ${id} = ${node}`;
if (owner.type === 'ConstTag') {
let child_scope = scope;
walk(node, {
enter(node: Node, parent: any) {
if (map.has(node)) child_scope = map.get(node);
if (node.type === 'Identifier' && is_reference(node, parent)) {
if (child_scope.has(node.name)) return;
this.replace(block.renderer.reference(node, ctx));
}
},
leave(node: Node) {
if (map.has(node)) child_scope = child_scope.parent;
}
});
} else if (dependencies.size === 0 && contextual_dependencies.size === 0) {
// we can hoist this out of the component completely
component.fully_hoisted.push(declaration);
this.replace(id as any);
component.add_var(node, {
name: id.name,
internal: true,
hoistable: true,
referenced: true
});
} else if (contextual_dependencies.size === 0) {
// function can be hoisted inside the component init
component.partly_hoisted.push(declaration);
block.renderer.add_to_context(id.name);
this.replace(block.renderer.reference(id));
} else {
// we need a combo block/init recipe
const extract_functions = () => {
const deps = Array.from(contextual_dependencies);
const function_expression = node as FunctionExpression;
@ -318,7 +284,7 @@ export default class Expression {
...function_expression.params
];
const context_args = deps.map(name => block.renderer.reference(name));
const context_args = deps.map(name => block.renderer.reference(name, ctx));
component.partly_hoisted.push(declaration);
@ -334,6 +300,50 @@ export default class Expression {
: b`function ${id}() {
return ${callee}(${context_args});
}`;
return { deps, func_declaration };
};
if (owner.type === 'ConstTag') {
// we need a combo block/init recipe
if (contextual_dependencies.size === 0) {
let child_scope = scope;
walk(node, {
enter(node: Node, parent: any) {
if (map.has(node)) child_scope = map.get(node);
if (node.type === 'Identifier' && is_reference(node, parent)) {
if (child_scope.has(node.name)) return;
this.replace(block.renderer.reference(node, ctx));
}
},
leave(node: Node) {
if (map.has(node)) child_scope = child_scope.parent;
}
});
} else {
const { func_declaration } = extract_functions();
this.replace(func_declaration[0]);
}
} else if (dependencies.size === 0 && contextual_dependencies.size === 0) {
// we can hoist this out of the component completely
component.fully_hoisted.push(declaration);
this.replace(id as any);
component.add_var(node, {
name: id.name,
internal: true,
hoistable: true,
referenced: true
});
} else if (contextual_dependencies.size === 0) {
// function can be hoisted inside the component init
component.partly_hoisted.push(declaration);
block.renderer.add_to_context(id.name);
this.replace(block.renderer.reference(id));
} else {
// we need a combo block/init recipe
const { deps, func_declaration } = extract_functions();
if (owner.type === 'Attribute' && owner.parent.name === 'slot') {
const dep_scopes = new Set<INode>(deps.map(name => template_scope.get_owner(name)));

@ -0,0 +1,34 @@
export default {
html: `
<div>[Y] A <button>Toggle</button></div>
<div>[N] B <button>Toggle</button></div>
<div>[N] C <button>Toggle</button></div>
`,
async test({ component, target, assert, window }) {
const [btn1, btn2, btn3] = target.querySelectorAll('button');
await btn1.dispatchEvent(new window.MouseEvent('click'));
await btn2.dispatchEvent(new window.MouseEvent('click'));
assert.htmlEqual(target.innerHTML, `
<div>[N] A <button>Toggle</button></div>
<div>[Y] B <button>Toggle</button></div>
<div>[N] C <button>Toggle</button></div>
`);
await btn2.dispatchEvent(new window.MouseEvent('click'));
assert.htmlEqual(target.innerHTML, `
<div>[N] A <button>Toggle</button></div>
<div>[N] B <button>Toggle</button></div>
<div>[N] C <button>Toggle</button></div>
`);
await btn3.dispatchEvent(new window.MouseEvent('click'));
assert.htmlEqual(target.innerHTML, `
<div>[N] A <button>Toggle</button></div>
<div>[N] B <button>Toggle</button></div>
<div>[Y] C <button>Toggle</button></div>
`);
}
};

@ -0,0 +1,16 @@
<script>
let items = [
{ name: 'A', selected: true },
{ name: 'B', selected: false },
{ name: 'C', selected: false },
]
</script>
{#each items as item}
{@const toggle = () => item.selected = !item.selected}
<div>
{item.selected ? '[Y]' : '[N]'}
{item.name}
<button on:click={toggle}>Toggle</button>
</div>
{/each}
Loading…
Cancel
Save