在Vuex中,我能否从getters中进行分发操作?

39

Fiddle : 这里

我正在使用Vue 2和Vuex创建一个Web应用程序。我有一个存储区,在那里我想从getter获取状态数据,我想要的是如果getter发现数据尚未填充,它会调用dispatch并获取数据。

以下是我的Vuex存储区:

const state = {
  pets: []
};

const mutations = {
  SET_PETS (state, response) {
    state.pets = response;
  }
};

const actions = {
 FETCH_PETS: (state) => {
      setTimeout(function() { 
            state.commit('SET_PETS', ['t7m12qbvb/apple_9', '6pat9znxz/1448127928_kiwi'])
    }, 1000)
 }
}

const getters = {
    pets(state){
    if(!state.pets.length){
        state.dispatch("FETCH_PETS")
    }
    return state.pets
  }
}

const store = new Vuex.Store({
  state,
  mutations,
  actions,
  getters
});

但是我遇到了以下错误:

未捕获的类型错误:state.dispatch不是一个函数(…)

我知道我可以在Vue组件的beforeMount中做到这一点,但我有多个使用相同Vuex store的组件,因此我必须在其中一个组件中执行它,那应该是哪个组件,并且它将如何影响其他组件。


我也在寻找答案。我理解GuyC的意思,但我希望返回的数据是一个大型下载,我不想在应用程序首次加载时触发它。多个组件需要它,但用户可能首先访问其中任何一个(或没有)。 - skagzilla
4个回答

24

由于Getter接收的是存储(state)的上下文(context),而非store的上下文(context),因此Getter不能调用dispatch函数。

Action可以调用state, dispatch, commit,因为它们接收了上下文(context)。

Getter用于管理“派生状态”。

如果您在需要它的组件上设置了'pets'状态,那么您只需从应用程序的根处调用FETCH_PETS并删除Getter的需要。


1
谢谢回复,有两件事情:1. 如果有多个组件使用宠物,应该在哪些组件中获取它?可以在哪个中心位置进行此操作? 2. 同样,这也不能从计算中完成吗? - Saurabh
  1. Vuex 是您的中央存储库,因此所有人都可以通过计算属性访问它,即 computed: { pets () { return this.$store.state.pets } } 将存储库传递到应用程序的根部允许这样做。
  2. 您可以使用应用程序根的 mounted 方法,但我不会使用计算属性对 vuex 进行异步更新。相反,它们可以观察存储库(如上所述),并在异步操作完成并提交其新宠物数据状态时进行更新。
- GuyC
为了澄清第一点,我建议在您的应用程序根目录或逻辑上首次调用宠物对象时被调用的组件中触发异步vuex dispatch()。没有看到应用程序的结构,很难确定具体位置,但我相信您会找到一个合适的位置。 - GuyC
1
我更新了你的JSFiddle以展示这个功能:https://jsfiddle.net/thebigsurf/9a6Lg2vd/7/ - GuyC

13

我知道这是一篇较旧的帖子,不确定这是否是一个好的实践方法,但我在我的存储模块中的getter中执行了以下操作:

import store from "../index"

并且在我的getter中使用store,如下所示:

store.dispatch("moduleName/actionName")

我这样做是为了确保数据在没有出现时也可以使用。

*编辑: 我想让你注意这个链接:Vue form - getters and side effects

这与@storsoc的注意事项有关。

如果您需要从getter中进行分派,则可能已经错误地实现了状态。也许更高级别的组件应该在之前已经获取了数据(状态提升)。此外,请注意只有在需要从当前状态派生其他数据以在模板中提供它之前才应使用getter,否则您可以直接调用状态:this.$store.state.variable 以在方法/计算属性中使用。

还要考虑您的生命周期方法...例如,在您的mounted或created方法中,您可以检查状态是否设置,否则从那里分派。如果您的getter /“直接状态”位于计算属性中,则应能够检测到更改。


1
绝对有用并且也为我们节省了很多时间。根据使用情况,需要注意触发冗余或循环API调用的问题,因为getter是同步的而分派的操作是异步的。解决方法是在存储中创建一个挂起操作的注册表(我们通过操作名称加上序列化数据参数进行哈希),如果操作挂起,则忽略该操作,在完成时删除挂起。虽然这种模式还不完全令人满意,但它适用于我们的即时获取-渲染需求,可以大大减少用户等待时间。 - storsoc

7

我曾经遇到过同样的问题..我也想让所有Vue实例自动加载某些内容,于是我写了一个混入:

store.registerModule('session', {
    namespaced: true,
    state: {
        session: {hasPermission:{}},
        sessionLoaded:false
    },
    mutations: {
        changeSession: function (state, value)
        {
            state.session = value;
        },
        changeSessionLoaded: function (state)
        {
            state.sessionLoaded = true;
        }

    },
    actions: {
        loadSession(context)
        {
            // your Ajax-request, that will set context.state.session=something
        }
    }
}); 

Vue.mixin({
    computed: {
        $session: function () { return this.$store.state.session.session; },
    },
    mounted:function()
    {
        if(this.$parent==undefined && !this.$store.state.session.sessionLoaded)
        {
            this.$store.dispatch("session/loadSession");
            this.$store.commit("changeSessionLoaded");
        }
    },
});

因为它仅在每个Vue实例和存储中加载一次,并且自动包含在每个Vue实例中,所以无需在每个主应用程序中定义它。

0
我使用getter来配置动态页面。基本上,就像这样:
getter: {
  configuration: function () {
    return {
      fields: [
        {
          component: 'PlainText',
          props: {},
          setPropsFromPageState: function (props, pageState, store) {
            // custom logic
          }
        }
      ]
    };
  }
}

然后在页面组件中,当我动态设置动态组件的props时,我可以调用setPropsFromPageState(field.props, this.details, this.$store)方法来为该组件设置逻辑,以修改传入的props的值,或者在需要时进行提交/分发。

基本上,这只是一个存储在getter中的回调函数,在组件上下文中执行,并通过它访问$store。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接