我的应用程序是一个内置于Vue with Vuex的简化电子表格。关键组件是TableCollection
,Table
和Row
。 TableCollection
有一个包含多个Table
个对象的数组。每个Table
都有一个包含多个Row
个对象的数组。
Row
组件有一个名为calculatedField
的属性。这简单地组合了行中的两个字段以产生第三个字段。我的选择是将calculatedField
实现为计算属性,Row
组件的本地属性,或者作为Vuex商店中的getter。
Table
组件需要subTotal
值,该值是通过为表中的所有行添加calculatedField
来计算的。如您所见,subTotal
计算取决于calculatedField
计算。
如果我将calculatedField
实现为Row
的本地计算属性,则会对其进行缓存。但问题是,我似乎无法从Table
父级访问计算字段。我在Table
中尝试了以下内容:
computed : {
subTotal : function () {
let total = 0;
this.table.rows.forEach(function (row) {
total += row.calculatedField;
});
return total;
}
}
结果是NaN。
一种解决方案是在calculatedField
的计算属性中复制Table
中的逻辑,但这不是DRY。
另一种选择是将subTotal
和calculatedField
同时作为商店中的getter实现,但这意味着将参数传递给gettter(tableId
,rowId
,或两者),因此结果不会被缓存。这似乎效率很低。
最后一种可能性是我在全局帮助器或mixin中实现我的calculatedField
逻辑。这样可以避免代码重复和getter效率低下,但感觉不太正确 - 代码与Table
和Row
具体相关,最好保留在那里。
我忽略了其他解决方案吗?什么是理想的“Vue-way”?
答案 0 :(得分:2)
如果性能问题且缓存现在很重要,您可能希望在Table
组件上实施缓存。
在行组件中,发出新值,以便父组件可以缓存它。
computed: {
calculatedField() {
const result = this.data.field + this.data.other;
this.$emit('change', this.data.id, result);
return result;
}
},
在表组件中,处理事件并缓存新值。
data() {
return { cache: {} };
},
computed: {
subTotal() {
return Object.values(this.cache).reduce((total, value) => total + value, 0);
}
},
methods: {
onChange(rowId, val) {
// important for reactivity
this.$set(this.cache, rowId, val);
}
},
当Row
的数据更新时,它会触发带有新计算值的更改事件,而父Table
会跟踪计算出的值,使用此缓存获取小计。< / p>
您可以在以下示例中看到计算属性被命中一次,然后在行更改(单击Rand按钮),只刷新相关的计算属性。
const MyRow = {
props: {
data: {
type: Object
}
},
computed: {
calculatedField() {
console.log("row computed for", this.data.id);
const result = this.data.field + this.data.other;
this.$emit('change', this.data.id, result);
return result;
}
},
methods: {
onClick() {
this.data.other = Math.floor(Math.random() * 10);
}
},
template: `
<tr>
<td>{{ data.field }}</td>
<td>{{ data.other }}</td>
<td>{{ calculatedField }}</td>
<td><button type="button" @click="onClick">Rand</button></td>
</tr>
`
};
const MyTable = {
props: {
rows: {
type: Array
}
},
components: {
MyRow
},
data() {
return {
cache: {}
}
},
computed: {
subTotal() {
console.log("Table subTotal");
return Object.values(this.cache).reduce((total, value) => total + value, 0);
}
},
methods: {
onChange(rowId, val) {
console.log("onChange", rowId, val);
this.$set(this.cache, rowId, val);
}
},
template: `
<div>
<table border="1">
<tr><th>field</th><th>other</th><th>calculated</th><th></th></tr>
<my-row v-for="row in rows" @change="onChange" :key="row.id" :data="row"></my-row>
</table>
Subtotal: {{ subTotal }}
</div>
`
};
var app = new Vue({
el: '#app',
components: {
MyTable
},
data: {
rows: [{
id: 1,
field: 1,
other: 1
},
{
id: 2,
field: 2,
other: 2
},
{
id: 3,
field: 3,
other: 3
},
]
},
template: `<my-table :rows="rows"></my-table>`
});
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>