implement full-state computed properties - fixes #1303

pull/1377/head
Rich Harris 7 years ago
parent ed605bfa79
commit 0dafc34de0

@ -564,16 +564,29 @@ export default class Compiler {
if (templateProperties.computed) { if (templateProperties.computed) {
const dependencies = new Map(); const dependencies = new Map();
const fullStateComputations = [];
templateProperties.computed.value.properties.forEach((prop: Node) => { templateProperties.computed.value.properties.forEach((prop: Node) => {
const key = getName(prop.key); const key = getName(prop.key);
const value = prop.value; const value = prop.value;
const deps = value.params[0].properties.map(prop => prop.key.name); addDeclaration(key, value, false, 'computed', {
state: true,
changed: true
});
const param = value.params[0];
if (param.type === 'ObjectPattern') {
const deps = param.properties.map(prop => prop.key.name);
deps.forEach(dep => { deps.forEach(dep => {
this.expectedProperties.add(dep); this.expectedProperties.add(dep);
}); });
dependencies.set(key, deps); dependencies.set(key, deps);
} else {
fullStateComputations.push({ key, deps: null })
}
}); });
const visited = new Set(); const visited = new Set();
@ -590,16 +603,15 @@ export default class Compiler {
computations.push({ key, deps }); computations.push({ key, deps });
const prop = templateProperties.computed.value.properties.find((prop: Node) => getName(prop.key) === key); const prop = templateProperties.computed.value.properties.find((prop: Node) => getName(prop.key) === key);
addDeclaration(key, prop.value, false, 'computed', {
state: true,
changed: true
});
}; };
templateProperties.computed.value.properties.forEach((prop: Node) => templateProperties.computed.value.properties.forEach((prop: Node) =>
visit(getName(prop.key)) visit(getName(prop.key))
); );
if (fullStateComputations.length > 0) {
computations.push(...fullStateComputations);
}
} }
if (templateProperties.data) { if (templateProperties.data) {

@ -64,10 +64,6 @@ export default function dom(
if (computations.length) { if (computations.length) {
computations.forEach(({ key, deps }) => { computations.forEach(({ key, deps }) => {
deps.forEach(dep => {
computationDeps.add(dep);
});
if (target.readonly.has(key)) { if (target.readonly.has(key)) {
// <svelte:window> bindings // <svelte:window> bindings
throw new Error( throw new Error(
@ -77,11 +73,22 @@ export default function dom(
target.readonly.add(key); target.readonly.add(key);
const condition = `${deps.map(dep => `changed.${dep}`).join(' || ')}`; if (deps) {
deps.forEach(dep => {
computationDeps.add(dep);
});
const condition = `${deps.map(dep => `changed.${dep}`).join(' || ')}`;
const statement = `if (this._differs(state.${key}, (state.${key} = %computed-${key}(state)))) changed.${key} = true;`; const statement = `if (this._differs(state.${key}, (state.${key} = %computed-${key}(state)))) changed.${key} = true;`;
computationBuilder.addConditional(condition, statement); computationBuilder.addConditional(condition, statement);
} else {
// computed property depends on entire state object —
// these must go at the end
computationBuilder.addLine(
`if (this._differs(state.${key}, (state.${key} = %computed-${key}(state)))) changed.${key} = true;`
);
}
}); });
} }

@ -120,8 +120,7 @@ export default function ssr(
ctx = Object.assign(${initialState.join(', ')}); ctx = Object.assign(${initialState.join(', ')});
${computations.map( ${computations.map(
({ key, deps }) => ({ key }) => `ctx.${key} = %computed-${key}(ctx);`
`ctx.${key} = %computed-${key}(ctx);`
)} )}
${target.bindings.length && ${target.bindings.length &&

@ -81,14 +81,5 @@ export default function computed(validator: Validator, prop: Node) {
message: `Computed properties must take a single argument` message: `Computed properties must take a single argument`
}); });
} }
const param = params[0];
if (param.type !== 'ObjectPattern') {
// TODO post-v2, allow the entire object to be passed in
validator.error(computation.value, {
code: `invalid-computed-argument`,
message: `Computed property argument must be a destructured object pattern`
});
}
}); });
} }

@ -0,0 +1,21 @@
export default {
data: { a: 1 },
html: `
<p>a: 1</p>
<p>x: 2</p>
<p>y: 4</p>
<p>z: 8</p>
`,
test(assert, component, target) {
component.set({ a: 2 });
assert.htmlEqual(target.innerHTML, `
<p>a: 2</p>
<p>x: 4</p>
<p>y: 8</p>
<p>z: 16</p>
`)
},
};

@ -0,0 +1,14 @@
<p>a: {a}</p>
<p>x: {x}</p>
<p>y: {y}</p>
<p>z: {z}</p>
<script>
export default {
computed: {
y: state => state.x * 2,
z: state => state.y * 2,
x: ({ a }) => a * 2
}
};
</script>
Loading…
Cancel
Save