当使用计算属性 (Vuex) 时,$store 属性不具有响应性。

40

我有一个 Vuex 存储,我正在将其注入到我的实例中:

import store from '../store';
    
const mainNav = new Vue({
  el: '#main-nav',
  store,
  components: { NavComponent }
});

我从该 store 在组件中创建了一个计算属性:

computed: {
  isWide() {
    return this.$store.state.nav.type === 'wide';
  }
}

这确实会在组件初始化时为模板创建this.isWide属性,但当存储值更新时,组件不会注册此更改 - 模板上仍然是旧值。

我在这里做错了什么?

8个回答

57

如果您正确设置了所有内容,您的代码应该能够正常运行:http://codepen.io/CodinCat/pen/RKZeZe?editors=1010

一个非常常见的错误是您没有提供状态的初始数据。

您应该明确声明状态的形状,因此不要使用以下方式:

state: {}

// or

state: {
  nav: {}
}

做这个:

state: {
  nav: {
    type: '...'
  },
  ...
}

否则属性将不具有响应性,就像这个例子一样:http://codepen.io/CodinCat/pen/ggxBEV?editors=1010

同时确保你已经更新了状态,也许问题只是你没有按照预期正确地更新状态。


太棒了 - 这是正确的答案。我假设这对所有对象都适用,而不仅仅是 Vuex? - babbaggeii
1
在组件数据中相同。如果没有提供初始状态,Vue 就无法将其转换为响应式的。 - CodinCat
请确保您已经更新了状态,也许问题只是您没有按照预期正确地更新状态。 - James Westgate
6
一个非常普遍的错误是你没有提供你的状态初始数据。 - Jivan

15

实际上,你需要使用Vue.set()函数使新属性具有响应性。

changeType () {
   Vue.set(this.$store.state.nav, 'type', 'wide');
}

参见此 CodePen:https://codepen.io/DedicatedManager/pen/JZgpaa

我制作了一个关于这个主题的完整屏幕捕获视频:youtu.be/8GHczab2Lbs


我确信这不是我的问题。绝望之下,我打开了你的视频。看到4:11的时候,我暂停了视频,因为我意识到由于最近的本地更改,对象中的属性实际上不存在。感谢你让我意识到这一点。 - MiniGod
很高兴我的评论有所帮助。 - Dedicated Managers
1
这对Vue 3有什么影响?我得到了“Vue未定义”的错误。 - sparkle
请问如何在Vu3中更改它?Vue.set似乎不起作用。 - Hrushikesh Rao
我尝试使用Vue.set,但它仍然无法更新 :( - Quinten C

10

当从你的 Vuex 存储中添加新属性到一个对象时,你应该使用

Vue.set(obj, 'newProp', 123)

或者用一个新的对象替换它

state.obj = { ...state.obj, newProp: 123 }

来自 Vuex 文档


如果你有多层数组,比如 something[0].inside[0].value,你会怎么做? - user3779015
我认为最好的方法是制作一个多级数组的tmp副本并进行更改。然后只需覆盖整个something - ssten

2

你不应该直接从组件方法中操作存储对象,而应该在存储对象上提交突变。 突变是具有监听器的函数,当调用时会跟踪更改的变量并传播更改。 如果您需要执行某些异步操作,例如将数据提交到服务器,您必须进一步定义存储操作,执行异步调用,然后从那里提交突变以更新状态对象。 我知道这听起来很烦人,但一旦您使用它,它就非常简单明了。

const store = new Vuex.Store({
  state: {
    nav: {
      type: 'not wide by default'
    }
  },
  mutations: {
    changeType: (state, type) => {
      state.nav.type = type;
    }
  }
})

Vue.component('NavComponent', {
  template: '<div>isWide?: {{isWide}}</div>',
  computed: {
    isWide () {
      return this.$store.state.nav.type === 'wide'
    }
  }
})

new Vue({
  el: '#app',
  store,
  methods: {
    changeType (type) {
      this.$store.commit('changeType', type);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.1.0/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>
<div id="app">
  <nav-component></nav-component>
  <button @click="changeType('wide')">
    Change to Wide
  </button>
  <button @click="changeType('narrow')">
    Change to Narrow
  </button>
</div>

https://codepen.io/zoransa/pen/KyqvWd?editors=1010

它还可以直接在 stat.nav 上工作,而不需要键入https://codepen.io/zoransa/pen/xPrLyW?editors=1010


它在CodePen上可以运行,但显然我没有正确设置它。 - Zoran
说实话,在我的Vue应用程序中,我不使用store mutations,因为通常有太多的事情需要从应用程序的太多地方以太多种不同的方式进行更改,如果每个情况都要编写一个函数,那将是一件非常麻烦的事情。 - Nick M
嗨Nick,不必将所有内容都放在store中,只需存储您在多个地方使用的数据,并让组件拥有自己的数据和通过计算属性访问store。对于更新,您仍然需要通过mutations进行发送。 - Zoran
似乎我也可以在组件内部更改存储中的数据(无需使用突变)。 - Nick M
是的,有些人实际上并不使用完整的 Vuex,而是做一些类似于在 window 级别上最简单的数据对象。let state = { user: 'John', age: 38};然后在 Vue 数据中只需放置 data() {state: state, something: 'whatever'}。 - Zoran

0

这是我解决问题的方法:

我有包含其他对象的状态对象。我尝试在每次发生突变时使用Vue.set,但这很混乱,而且仍然不起作用。 因此,我创建了一个名为toggleState的布尔状态变量,并为其提供了getter。每当突变更改主状态对象时,它还会执行state.stateToggle =!state.stateToggle。 关心的任何人都可以关注stateToggle的变化,并进行所需的操作。在我的情况下,它正在使用突变的主对象重新构建树视图。


0

这是我的问题。

const store = new Vuex.Store({
  state: {
    isTrackPlaying: false,
  },
  ...
});

我忘记设置初始状态,现在它是响应式的。


0
在我的情况下,我只是忘记在我的mutation中传递变量'state',因此它没有响应。
const mutations = {
  [mutationTypes.storeTaskStart](state) {
    state.isSubmitting = true;
  },
};

-1

两者都是响应式的

$vm.$set

this.$set(some_object, 'key', 'value')

Vue.set

Vue.set(some_object, 'key', 'value')

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