为什么我的模板不会因为绑定到计算 属性 而更新?
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
},
- getter:
this.$store.getters['designer/getNumSetsForExercise']
this.id
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 现在正确检测更改,并防止以前的陈旧缓存问题。
我遇到一个问题,我在依赖于 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
},
- getter:
this.$store.getters['designer/getNumSetsForExercise']
this.id
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 现在正确检测更改,并防止以前的陈旧缓存问题。