带参数的 Vuex 映射 Getter - 已缓存?

Vuex Mapping Getter with Argument - Cached?

这是一个 Vuex 商店的示例,其中包含 参数化 getter,我需要将其映射到 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?

这是反模式吗?这个例子不是真实的,但我确实想在商店中集中逻辑。

更新

我更新了示例以说明对 inRange getter 所基于的基础 lower/higher 值的修改对于 Vue 实例确实是反应性的(尽管不是映射为状态)。我还包含了一个 unrelated 值,它不是计算的一部分,如果映射的 getter 被缓存,修改不相关的值不应触发再次调用 getter ,但是它

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

关于此模式中是否存在任何缺陷或性能更好的可用模式的问题仍然悬而未决。

在我看来,这是一种反模式。这是一种漏斗方法的奇怪方式。另外,不,这里没有缓存,因为 inRange 立即 return 一个值(最终函数)而不使用 state 中的任何成员 - 所以 Vue 检测到 0 个反应依赖项。

getter 不能以这种方式参数化,它们只能派生基于状态的东西。因此,如果范围可以存储在状态中,那将起作用(并且将被缓存)。

这里有类似的问题:

既然你想集中这个行为——我认为你应该在一个单独的模块中做这件事,也许作为一个混合。这也不会被缓存,因此您必须将它(和输入)包装在组件的 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);
    }
  }
});

只是为了说明为什么我接受了 Matt 的 ,这是一个工作片段,要注意的关键点是:

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>

似乎有办法解决这个问题,使用计算值创建一个地图并将其作为

访问
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>