Vuex Mapping getter with Argument - 缓存?

时间:2017-06-15 19:53:07

标签: javascript vue.js vuejs2 vuex

以下是带有参数化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,但它确实如此。

我的结论是没有缓存,因此它的性能比传统的计算属性差,但它仍然在功能上是正确的。

关于这种模式中是否存在任何缺陷,或者哪种缺陷表现更好,仍然存在问题。

3 个答案:

答案 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>