bail out of change tracking on encountering function calls in templates. TODO avoid bailing where possible

pull/1864/head
Rich Harris 7 years ago
parent 2fc75429e8
commit eb6f7c392c

@ -360,6 +360,11 @@ export default class Component {
}); });
} }
findDependenciesForFunctionCall(name) {
// TODO
return null;
}
walkJs() { walkJs() {
const { js } = this.ast; const { js } = this.ast;
if (!js) return; if (!js) return;

@ -4,6 +4,7 @@ import isReference from 'is-reference';
import flattenReference from '../../../utils/flattenReference'; import flattenReference from '../../../utils/flattenReference';
import { createScopes } from '../../../utils/annotateWithScopes'; import { createScopes } from '../../../utils/annotateWithScopes';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import addToSet from '../../../utils/addToSet';
const binaryOperators: Record<string, number> = { const binaryOperators: Record<string, number> = {
'**': 15, '**': 15,
@ -106,6 +107,19 @@ export default class Expression {
expression.thisReferences.push(node); expression.thisReferences.push(node);
} }
if (node.type === 'CallExpression') {
if (node.callee.type === 'Identifier') {
const dependencies_for_invocation = component.findDependenciesForFunctionCall(node.callee.name);
if (dependencies_for_invocation) {
addToSet(dependencies, dependencies_for_invocation);
} else {
dependencies.add('$$BAIL$$');
}
} else {
dependencies.add('$$BAIL$$');
}
}
if (isReference(node, parent)) { if (isReference(node, parent)) {
const { name, nodes } = flattenReference(node); const { name, nodes } = flattenReference(node);

@ -427,6 +427,8 @@ export default class EachBlockWrapper extends Wrapper {
} }
// TODO do this for keyed blocks as well // TODO do this for keyed blocks as well
const bail = allDependencies.has('$$BAIL$$');
const condition = Array.from(allDependencies) const condition = Array.from(allDependencies)
.map(dependency => `changed.${dependency}`) .map(dependency => `changed.${dependency}`)
.join(' || '); .join(' || ');
@ -476,7 +478,7 @@ export default class EachBlockWrapper extends Wrapper {
`; `;
} }
block.builders.update.addBlock(deindent` const update = deindent`
if (${condition}) { if (${condition}) {
${this.vars.each_block_value} = ${snippet}; ${this.vars.each_block_value} = ${snippet};
@ -488,7 +490,17 @@ export default class EachBlockWrapper extends Wrapper {
${destroy} ${destroy}
} }
`); `;
if (bail) {
block.builders.update.addBlock(update);
} else {
block.builders.update.addBlock(deindent`
if (${condition}) {
${update}
}
`);
}
} }
if (outroBlock && this.renderer.component.options.nestedTransitions) { if (outroBlock && this.renderer.component.options.nestedTransitions) {

@ -99,7 +99,7 @@ export default class AttributeWrapper {
const isSelectValueAttribute = const isSelectValueAttribute =
name === 'value' && element.node.name === 'select'; name === 'value' && element.node.name === 'select';
const shouldCache = this.node.shouldCache || isSelectValueAttribute; const shouldCache = !this.node.dependencies.has('$$BAIL$$') && (this.node.shouldCache || isSelectValueAttribute);
const last = shouldCache && block.getUniqueName( const last = shouldCache && block.getUniqueName(
`${element.var}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value` `${element.var}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value`

@ -44,16 +44,22 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
.join(' + '); .join(' + ');
if (propDependencies.size) { if (propDependencies.size) {
const dependencies = Array.from(propDependencies); if (propDependencies.has('$$BAIL$$')) {
const condition = ( block.builders.update.addLine(
(block.hasOutros ? `!#current || ` : '') + `@setStyle(${this.parent.var}, "${prop.key}", ${value});`
dependencies.map(dependency => `changed.${dependency}`).join(' || ') );
); } else {
const dependencies = Array.from(propDependencies);
block.builders.update.addConditional( const condition = (
condition, (block.hasOutros ? `!#current || ` : '') +
`@setStyle(${this.parent.var}, "${prop.key}", ${value});` dependencies.map(dependency => `changed.${dependency}`).join(' || ')
); );
block.builders.update.addConditional(
condition,
`@setStyle(${this.parent.var}, "${prop.key}", ${value});`
);
}
} }
} else { } else {
value = stringify(prop.value[0].data); value = stringify(prop.value[0].data);

@ -588,7 +588,7 @@ export default class ElementWrapper extends Wrapper {
this.node.attributes this.node.attributes
.filter(attr => attr.type === 'Attribute' || attr.type === 'Spread') .filter(attr => attr.type === 'Attribute' || attr.type === 'Spread')
.forEach(attr => { .forEach(attr => {
const condition = attr.dependencies.size > 0 const condition = (attr.dependencies.size > 0 && !attr.dependencies.has('$$BAIL$$'))
? `(${[...attr.dependencies].map(d => `changed.${d}`).join(' || ')})` ? `(${[...attr.dependencies].map(d => `changed.${d}`).join(' || ')})`
: null; : null;

@ -134,6 +134,8 @@ export default class InlineComponentWrapper extends Wrapper {
this.node.attributes.forEach(attr => { this.node.attributes.forEach(attr => {
const { name, dependencies } = attr; const { name, dependencies } = attr;
// TODO probably need to account for $$BAIL$$ but
// not totally sure how. will come back to it
const condition = dependencies.size > 0 && (dependencies.size !== allDependencies.size) const condition = dependencies.size > 0 && (dependencies.size !== allDependencies.size)
? `(${[...dependencies].map(d => `changed.${d}`).join(' || ')})` ? `(${[...dependencies].map(d => `changed.${d}`).join(' || ')})`
: null; : null;

@ -48,9 +48,13 @@ export default class Tag extends Wrapper {
const updateCachedValue = `${value} !== (${value} = ${snippet})`; const updateCachedValue = `${value} !== (${value} = ${snippet})`;
const condition = this.node.shouldCache ? const condition = dependencies.has('$$BAIL$$')
(dependencies.size ? `(${changedCheck}) && ${updateCachedValue}` : updateCachedValue) : ? updateCachedValue
changedCheck; : this.node.shouldCache
? dependencies.size > 0
? `(${changedCheck}) && ${updateCachedValue}`
: updateCachedValue
: changedCheck;
block.builders.update.addConditional( block.builders.update.addConditional(
condition, condition,

@ -35,7 +35,8 @@ export default class CodeBuilder {
addConditional(condition: string, body: string) { addConditional(condition: string, body: string) {
this.reifyConditions(); this.reifyConditions();
body = body.replace(/^/gm, `${this.indent}\t`); const indent = this.indent + (condition ? '\t' : '');
body = body.replace(/^/gm, indent);
if (condition === this.lastCondition) { if (condition === this.lastCondition) {
this.result += `\n${body}`; this.result += `\n${body}`;
@ -44,7 +45,11 @@ export default class CodeBuilder {
this.result += `\n${this.indent}}`; this.result += `\n${this.indent}}`;
} }
this.result += `${this.last === ChunkType.Block ? '\n\n' : '\n'}${this.indent}if (${condition}) {\n${body}`; const block = condition
? `if (${condition}) {\n${body}`
: body;
this.result += `${this.last === ChunkType.Block ? '\n\n' : '\n'}${this.indent}${block}`;
this.lastCondition = condition; this.lastCondition = condition;
} }

@ -1,10 +1,16 @@
export default { export default {
html: '<p>1 + 2 = 3</p>\n<p>3 * 3 = 9</p>', html: `
<p>1 + 2 = 3</p>
<p>3 * 3 = 9</p>
`,
test(assert, component, target) { test(assert, component, target) {
component.a = 3; component.a = 3;
assert.equal(component.c, 5); assert.equal(component.c(), 5);
assert.equal(component.cSquared, 25); assert.equal(component.cSquared(), 25);
assert.equal(target.innerHTML, '<p>3 + 2 = 5</p>\n<p>5 * 5 = 25</p>'); assert.htmlEqual(target.innerHTML, `
<p>3 + 2 = 5</p>
<p>5 * 5 = 25</p>
`);
} }
}; };

@ -2,13 +2,11 @@
export let a = 1; export let a = 1;
export let b = 2; export let b = 2;
function c() { export function c() {
return a + b; return a + b;
} }
function cSquared() { export const cSquared = () => c() * c();
return c() * c();
}
</script> </script>
<p>{a} + {b} = {c()}</p> <p>{a} + {b} = {c()}</p>

Loading…
Cancel
Save