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>