Vue и Vuex: обработка зависимых вычисляемых свойств
Мое приложение-это сокращенная электронная таблица, встроенная в Vue с Vuex. Ключевыми компонентами являются:TableCollection
, Table
и Row
. TableCollection
имеет массив с несколькими объектами Table
. Каждый Table
имеет массив с несколькими объектами Row
.
Компонент Row
имеет свойство с именем calculatedField
. Это просто объединяет два поля в строке, чтобы создать третье поле. Мои варианты-реализовать calculatedField
как вычисляемое свойство, локальное для компонента Row
, или как геттер в Vuex магазин.
Компонент 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;
}
}
В результате была Нэн.
Одно решение было бы следует дублировать логику из calculatedField
в вычисляемом свойстве Table
, но это не сухо.
Другой альтернативой было бы реализовать как subTotal
, так и calculatedField
в качестве геттеров в хранилище, однако это означало бы передачу аргументов геттеру (tableId
, rowId
, или и то и другое), и поэтому результаты не будут кэшироваться. Это кажется действительно неэффективным.
calculatedField
в глобальном помощнике или миксине. Это позволит избежать дублирования кода и геттера неэффективность, но не совсем правильно-код относится конкретно к Table
и Row
, и в идеале будет сохранен там.
Есть ли другие решения, которые я упустил из виду? Что такое идеальный "Vue-way"?1 ответ:
Если производительность является проблемой и кэширование важно прямо сейчас, вы можете реализовать кэширование на компоненте
Table
.В компонентестроки выделите новое значение, чтобы родительский компонент мог кэшировать его.
computed: { calculatedField() { const result = this.data.field + this.data.other; this.$emit('change', this.data.id, result); return result; } },
В компонентеTable обработайте событие и кэшируйте новые значения.
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
отслеживает вычисленное значение. значения, используя этот кэш для получения промежуточного итога.В следующем примере можно увидеть, что вычисляемые свойства выполняются один раз, а затем при изменении строки (нажмите кнопку 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>