为什么我的模板不会因为绑定到计算 属性 而更新?

Why won't my template update with it being bound to a computed property?

我遇到一个问题,我在依赖于 Vuex 方法的 computed getter 的组件中有一些模板 HTML。正如您在模板中看到的,我只是试图在带有 {{ getNumSets }}.

<p> 标记中显示计算出的 属性 的输出

当我使用 UPDATE_EXERCISE_SETS 突变更新状态时,我可以在 Vue devtools 中看到状态已正确更新,但更改未反映在 <p> {{ getNumSets }} </p> 部分。

模板HTML:

<template>
...
<v-text-field
   v-model="getNumSets"
   placeholder="S"
   type="number"
   outlined
   dense
></v-text-field>
<p>{{ getNumSets }}</p>
...
</template>

组件逻辑:

<script>
...
computed: {
   getNumSets: {
      get() {
         var numSets = this.$store.getters['designer/getNumSetsForExercise']({id: this.id, parent: this.parent})
         return numSets
      },
      set(value) {  // This correctly updates the state as seen in the Vue DevTools
        this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
                    id: this.exerciseId,
                    parentName: this.parent,
                    numSets: parseInt(value),
                    date: this.date
                })
      }

}
...
</script>

Vuex 存储逻辑:

...
state: {
  designerBucket: []
},
getters: {
  getNumSetsForExercise: (state) => (payload) => {
    var numSets = 0
    for (var i = 0; i < state.designerBucket.length; i++) {
      if (state.designerBucket[i].id == payload.id) {
        numSets = state.designerBucket[i].numSets
      }
    }
    return numSets
  }
},
mutations: {
  UPDATE_EXERCISE_SETS(state, payload) {
    state.designerBucket.forEach(exercise => {
       if (exercise.id == payload.id) {
          exercise.numSets = payload.numSets
       }
    })
   }
}

非常感谢任何见解!

P.S。我也尝试过使用 for (var i=0...) 循环,遍历索引然后使用 Vue.set() 设置值。这确实也更新了商店中的值,但是计算的 属性 仍然没有更新模板。

已编辑:首先在脚本标签顶部像这样从 'vuex' 导入 mapGetters。

import { mapGetters } from "vuex"

现在在您的计算对象中,添加 mapGetter 并将参数传递给 get() 方法中的 getter 方法,如下所示:-

computed: {
   ...mapGetters('designer',['getNumSetsForExercise']),
   getNumSets: {
      get() {
         var numSets = this.getNumSetsForExercise({id: this.id, parent: this.parent})
         return numSets
      },
      set(value) {  // This correctly updates the state as seen in the Vue DevTools
        this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
                    id: this.exerciseId,
                    parentName: this.parent,
                    numSets: parseInt(value),
                    date: this.date
                })
      }

}

看看它是否有效。

这变成了一个冗长的回答,但请耐心等待。

这是我的直觉:由于您正在从您的 Vuex getter 中 returning 一个 函数,Vue 不会更新您的计算 属性 状态改变,因为 函数永远不会改变 ,即使从它编辑的值 return 会改变。这破坏了计算属性的缓存机制。


箭头函数吸气剂的反应性


创建像这样的 getter 时要记住的事情之一,其中 return 一个箭头函数:

getNumSetsForExercise: (state) => (payload) => {
    var numSets = 0
    for (var i = 0; i < state.designerBucket.length; i++) {
        if (state.designerBucket[i].id == payload.id) {
        numSets = state.designerBucket[i].numSets
        }
    }
    return numSets
}

...是您不再 return 从您的 getter 获取实际状态数据。

当您使用它从依赖于组件本地数据的状态中提取某些内容时,这非常有用,因为我们不需要 Vue 来检测更改,我们只需要访问当前状态的函数,它做的很好。

BUT,这也可能导致误以为更新状态应该更新 getter,而实际上并没有。由于 computed 属性如何 跟踪它们的依赖性 缓存数据.

计算缓存和依赖检测


在 Vue 中,computed 属性比乍看起来更智能。他们缓存他们的结果,他们注册并跟踪他们所依赖的反应值,以了解何时使该缓存无效。

一旦 Vue 计算出计算 属性 的值,它就会在内部存储它,因此如果您再次调用 属性 而不更改依赖项, 属性 可以 return 缓存值而不是重新计算。

你的案例的关键是依赖检测——你的 getter 有 Vue 检测到的三个依赖:

get() {
    var numSets = this.$store.getters['designer/getNumSetsForExercise']({id: this.id, parent: this.parent})
    return numSets
},
  1. getter:this.$store.getters['designer/getNumSetsForExercise']
  2. this.id
  3. this.parent

None<v-text-field> 调用您的 setter.

这意味着 Vue 没有检测到任何依赖项更改,它正在 return 缓存数据而不是重新计算。

如何修复?


通常,当您 运行 陷入此类依赖性问题时,是因为状态的设计可以改进,无论是通过将更多数据移入状态,还是通过某种方式重构它。

在这种情况下,除非您绝对需要 designerBucket 是一个用于排序目的的数组,否则我建议将其设为 object 相反,每个集合由 id 存储。这将通过删除循环来简化实现,并且完全不需要 getter:

...
state: {
  designerBucket: {}
},
mutations: {
  UPDATE_EXERCISE_SETS(state, payload) {
    // Need to use $set since we're adding a new property to the object
    Vue.set(state.designerBucket, payload.id, payload.numSets);
  }
}

现在,不用调用 getter,只需从状态中拉出 designerBucket 并直接通过 this.id 访问:

<script>
...
computed: {
    getNumSets: {
    get() {
        return this.$store.state.designerBucket[this.id];
    },
    set(value) {
        this.$store.commit('designer/UPDATE_EXERCISE_SETS', {
            id: this.exerciseId,
            parentName: this.parent,
            numSets: parseInt(value),
            date: this.date
        });
    }
}
...
</script>

应该 允许 Vue 现在正确检测更改,并防止以前的陈旧缓存问题。