[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,8 +274,38 @@ export default class Expression {
);
const declaration = b`const ${id} = ${node}`;
const extract_functions = () => {
const deps = Array.from(contextual_dependencies);
const function_expression = node as FunctionExpression;
const has_args = function_expression.params.length > 0;
function_expression.params = [
...deps.map(name => ({ type: 'Identifier', name } as Identifier)),
...function_expression.params
];
const context_args = deps.map(name => block.renderer.reference(name, ctx));
component.partly_hoisted.push(declaration);
block.renderer.add_to_context(id.name);
const callee = block.renderer.reference(id);
this.replace(id as any);
const func_declaration = has_args
? b`function ${id}(...args) {
return ${callee}(${context_args}, ...args);
}`
: 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) {
@ -289,6 +319,10 @@ export default class Expression {
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);
@ -309,31 +343,7 @@ export default class Expression {
this.replace(block.renderer.reference(id));
} else {
// we need a combo block/init recipe
const deps = Array.from(contextual_dependencies);
const function_expression = node as FunctionExpression;
const has_args = function_expression.params.length > 0;
function_expression.params = [
...deps.map(name => ({ type: 'Identifier', name } as Identifier)),
...function_expression.params
];
const context_args = deps.map(name => block.renderer.reference(name));
component.partly_hoisted.push(declaration);
block.renderer.add_to_context(id.name);
const callee = block.renderer.reference(id);
this.replace(id as any);
const func_declaration = has_args
? b`function ${id}(...args) {
return ${callee}(${context_args}, ...args);
}`
: b`function ${id}() {
return ${callee}(${context_args});
}`;
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