以下是带有参数化getter 的Vuex商店的示例,我需要将其映射到Vue实例以在模板中使用。
const store = new Vuex.Store({
state: {
lower: 5,
higher: 10,
unrelated: 3
},
getters: {
inRange: state => value => {
console.log('inRange run')
return (state.lower <= value) && (state.higher >= value)
}
},
mutations: {
reduceLower: state => state.lower--,
incrementUnrelated: state => state.unrelated++
}
})
new Vue({
el: '#app',
template: "<div>{{ inRange(4) }}, {{ unrelated }}</div>",
store,
computed: Object.assign(
Vuex.mapGetters(['inRange']),
Vuex.mapState(['unrelated'])
),
})
setTimeout(function() {
console.log('reduceLower')
store.commit('reduceLower')
setTimeout(function() {
console.log('incrementUnrelated')
store.commit('incrementUnrelated')
}, 3000);
}, 3000);
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex/dist/vuex.js"></script>
<div id="app"></div>
首先,这似乎是有效的,有效的代码。但是考虑到computed
是一个缓存的计算属性集,我很好奇这个场景中的行为,是否有缓存?如果没有,是否需要考虑性能问题?即使该函数不会导致任何状态更改,它应该是method
吗?
这是反模式吗?这个例子不是真实的,但我确实希望在商店中集中逻辑。
更新
我更新了示例,以说明对lower/higher
getter所基于的基础inRange
值的修改确实对Vue实例有反应(尽管没有被映射为状态)。我还包含一个unrelated
值,它不是计算的一部分,如果映射的getter被缓存,修改不相关的值不应该触发再次调用getter,但它确实如此。
我的结论是没有缓存,因此它的性能比传统的计算属性差,但它仍然在功能上是正确的。
关于这种模式中是否存在任何缺陷,或者哪种缺陷表现更好,仍然存在问题。
答案 0 :(得分:3)
在我看来,这是一种反模式。这是一种漏报方法的奇怪方法。此外,不,这里没有缓存,因为inRange
不使用state
中的任何成员立即返回值(最终函数) - 因此Vue检测到0个反应依赖项。
Getters不能以这种方式参数化,它们只能派生出基于状态的东西。因此,如果范围可以存储在状态中,那么它将起作用(并且将被缓存)。
类似的问题:vuexjs getter with argument
由于你想要集中这种行为 - 我认为你应该在一个单独的模块中这样做,也许作为一个混合。这也不会被缓存,因此您必须将它(和输入)包装在组件的computed
中或使用其他一些memoization
这样的事情:
import { inRange } from './state/methods';
import { mapGetters } from 'vuex';
const Example = Vue.extend({
data: {
rangeInput: 10
},
computed: {
...mapGetters(['lower', 'higher']),
inRange() {
return inRange(this.rangeInput, this.lower, this.higher);
}
}
});
答案 1 :(得分:2)
为了说明为什么我接受了马特的answer,这是一个工作片段,要注意的关键点不是:
Vuex.mapGetters(['inRange'])
有 true 计算属性:
inRange4: function() {
return this.$store.getters.inRange(4);
}
从运行代码段可以看出,这导致值被正确缓存。正如我所说,这个模式不是我可以使用的模式,因为我最终会得到太多的计算属性(inRange1,inRange2,inRange3等),但它确实回答了有问题的例子的问题。
我选择继续使用问题中的代码未更改。
注意: Matt的答案与此代码完全不符,我相信他的意图是来自商店的状态将映射到Vue实例,我认为这是不必要的。
const store = new Vuex.Store({
state: {
lower: 5,
higher: 10,
unrelated: 3
},
getters: {
inRange: state => value => {
console.log('inRange run')
return (state.lower <= value) && (state.higher >= value)
}
},
mutations: {
reduceLower: state => state.lower--,
incrementUnrelated: state => state.unrelated++
}
})
new Vue({
el: '#app',
template: "<div>{{ inRange4 }}, {{ unrelated }}</div>",
store,
computed: Object.assign(
{
inRange4: function() {
return this.$store.getters.inRange(4);
}
},
Vuex.mapState(['unrelated'])
),
})
setTimeout(function() {
console.log('reduceLower')
store.commit('reduceLower')
setTimeout(function() {
console.log('incrementUnrelated')
store.commit('incrementUnrelated')
}, 3000);
}, 3000);
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex/dist/vuex.js"></script>
<div id="app"></div>
答案 2 :(得分:0)
似乎可以解决此问题,使用计算出的值创建地图并将其作为
inrange[4];
我经常使用它来初始化不同类型的访问器,我从后端得到一个数组,并且需要通过某些字段(例如ID)来访问它。 对于上面的示例,由于范围很小,因此似乎是合理的:
const store = new Vuex.Store({
state: {
lower: 5,
higher: 10,
unrelated: 3
},
getters: {
inRange: state => {
console.log('inRange run')
var result = {};
for( var i = state.lower; i < state.higher; i++) {
result[i] = true;
}
return result;
}
},
mutations: {
reduceLower: state => state.lower--,
incrementUnrelated: state => state.unrelated++
}
})
new Vue({
el: '#app',
template: "<div>{{ inRange[4] }}, {{ unrelated }}</div>",
store,
computed: Object.assign(
{
inRange: function() {
return this.$store.getters.inRange;
}
},
Vuex.mapState(['unrelated'])
),
})
setTimeout(function() {
console.log('reduceLower')
store.commit('reduceLower')
setTimeout(function() {
console.log('incrementUnrelated')
store.commit('incrementUnrelated')
}, 3000);
}, 3000);
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex/dist/vuex.js"></script>
<div id="app"></div>