Vue 和 Vuex:处理依赖的计算属性 [英] Vue and Vuex: Handling dependent computed properties

查看:21
本文介绍了Vue 和 Vuex:处理依赖的计算属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序是使用 Vuex 在 Vue 中构建的精简电子表格.关键组件是TableCollectionTableRow.TableCollection 有一个包含多个 Table 对象的数组.每个 Table 都有一个包含多个 Row 对象的数组.

Row 组件有一个名为 calculatedField 的属性.这只是将行中的两个字段组合在一起以生成第三个字段.我的选择是将 calculatedField 实现为计算属性、本地 Row 组件,或作为 Vuex 存储中的 getter.

Table 组件需要一个 subTotal 值,该值是通过为表中的所有行添加 calculatedField 来计算的.如您所见,subTotal 计算依赖于 calculatedField 计算.

如果我将 calculatedField 实现为 Row 的本地计算属性,它会被缓存.然而,问题是我似乎无法从 Table 父级访问计算字段.我在 Table 中尝试了以下内容:

计算:{小计:函数(){让总数 = 0;this.table.rows.forEach(函数(行){总计 += row.calculatedField;});总回报;}}

结果是 NaN.

一种解决方案是在 Table 的计算属性中复制 calculatedField 的逻辑,但这不是 DRY.

另一种选择是将 subTotalcalculatedField 都实现为 store 中的 getter,但这意味着将参数传递给 getter (tableIdcode>、rowId 或两者),因此不会缓存结果.这看起来效率很低.

最后一种可能性是我在全局助手或 mixin 中实现我的 calculatedField 逻辑.这将避免代码重复和 getter 效率低下,但感觉不太正确 - 代码专门与 TableRow 相关,并且最好保留在那里.

还有其他我忽略的解决方案吗?什么是理想的Vue 方式"?

解决方案

如果性能是一个问题并且缓存现在很重要,您可能希望Table 组件上实现缓存.

Row 组件中,发出新值以便父组件可以缓存它.

 计算:{计算字段(){const 结果 = this.data.field + this.data.other;this.$emit('change', this.data.id, result);返回结果;}},

Table 组件中,处理事件并缓存新值.

 数据(){返回{缓存:{}};},计算:{小计(){return Object.values(this.cache).reduce((total, value) => total + value, 0);}},方法: {onChange(rowId, val) {//对反应性很重要this.$set(this.cache, rowId, val);}},

当一个 Row 的数据被更新时,它会触发一个带有新计算值的更改事件,并且父 Table 使用这个缓存来跟踪计算值得到小计.

您可以在以下示例中看到计算属性被命中一次,然后在行更改时(单击 Rand 按钮),仅刷新相关的计算属性.

const MyRow = {道具: {数据: {类型:对象}},计算:{计算字段(){console.log("计算的行", this.data.id);const 结果 = this.data.field + this.data.other;this.$emit('change', this.data.id, result);返回结果;}},方法: {点击(){this.data.other = Math.floor(Math.random() * 10);}},模板:`<tr><td>{{ data.field }}</td><td>{{ data.other }}</td><td>{{calculatedField}}</td><td><button type="button" @click="onClick">Rand</button></td></tr>`};const MyTable = {道具: {行:{类型:数组}},成分: {我的行},数据() {返回 {缓存:{}}},计算:{小计(){console.log("表小计");return Object.values(this.cache).reduce((total, value) => total + value, 0);}},方法: {onChange(rowId, val) {console.log("onChange", rowId, val);this.$set(this.cache, rowId, val);}},模板:`<div><表格边框=1"><tr><th>字段</th><th>other</th><th>计算的</th><th></th></tr><my-row v-for="row in rows" @change="onChange" :key="row.id" :data="row"></my-row>小计:{{ 小计}}

`};var app = new Vue({el: '#app',成分: {我的表},数据: {行:[{编号:1,领域:1,其他:1},{编号:2,领域:2,其他:2},{编号:3,领域:3,其他:3},]},模板:`<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>

My application is a cut-down spreadsheet built in Vue with Vuex. The key components are TableCollection, Table and Row. The TableCollection has an array with multiple Table objects. Each Table has an array with multiple Row objects.

The Row component has a property named calculatedField. This simply combines two fields in the row to produce a third field. My options are to implement calculatedField as a computed property, local to the Row component, or as a getter in the Vuex store.

The Table component requires a subTotal value, which is calculated by adding the calculatedField for all rows in the table. As you can see, the subTotal calculation is dependent on the calculatedField calculation.

If I implement calculatedField as a local computed property of Row, it is cached. The problem is however that I can't seem to access the calculated field from a Table parent. I tried the following in Table:

computed : {
    subTotal : function () {
        let total = 0;
        this.table.rows.forEach(function (row) {
           total += row.calculatedField;
        });
        return total;
    }
}

The result was NaN.

One solution would be to duplicate the logic from calculatedField in the computed property of Table, but that's not DRY.

The other alternative would be to implement both subTotal and calculatedField as getters in the store, however this would mean passing arguments to the gettter (tableId, rowId, or both), and so the results would not be cached. This seems really inefficient.

The final possibility is that I implement my calculatedField logic in a global helper or mixin. This would avoid code duplication and getter inefficiency, but doesn't feel quite right - the code relates specifically to Table and Row, and would ideally be kept there.

Are there other solutions I have overlooked? What is the ideal 'Vue-way'?

解决方案

If performance is an issue and caching is important right now, you might want to implement caching on the Table component.

In the Row component, emit the new value so the parent component can cache it.

  computed: {
    calculatedField() {
      const result = this.data.field + this.data.other;
      this.$emit('change', this.data.id, result);
      return result;
    }
  },

In the Table component, handle the event and cache the new values.

  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);
    }
  },

When a Row's data is updated, it triggers a change event with the new calculated value and the parent Table keeps track of the calculated values, using this cache to get the subtotal.

You can see in the following example that the computed properties are hit once, then on row changes (click the Rand button), only the relevant computed properties are refreshed.

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>

这篇关于Vue 和 Vuex:处理依赖的计算属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆