vue.js - vuex - 即使不推荐,是否可以直接更改状态?

标签 vue.js vuex

文档 here 说,

You cannot directly mutate the store's state. The only way to change a store's state is by explicitly committing mutations.

我的问题是,这是好的做法,还是 Vuex 状态的内部运作方式?换句话说,Vuex 状态是否以与 Vue 数据相同的方式 react (它将 JS 对象转换为可观察对象),还是其他方式?

一个类似的问题——你能直接改变一个 Action 中的状态而不是创建一个突变吗?我知道这是不好的做法,它失去了一些遵循约定所提供的可追溯性 - 但它有效吗?

最佳答案

Could you directly change the state in an action instead of creating a mutation? I know it's bad practice and it loses some of the traceability that following the conventions gives - but does it work?

有效,但抛出警告和错误。

vue.js:584 [Vue warn]: Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] Do not mutate vuex store state outside mutation handlers."

   (found in <Component>)
   warn @ vue.js:584
   ...

vue.js:1719 Error: [vuex] Do not mutate vuex store state outside mutation handlers.
    at assert (VM260 vuex.js:103)

谁知道在这之后还有什么可能会被破坏。

自己看看(注意模板中的数据更新):

const store = new Vuex.Store({
strict: true,
  state: {
    people: []
  },
  mutations: {
    populate: function (state, data) {
      //Vue.set(state, 'people', data);
    }
  }
});
new Vue({
  store,
  el: '#app',
  mounted: function() {
    let self = this;
    this.$http.get('https://api.myjson.com/bins/g07qh').then(function (response) {
      // setting without commit
      Vue.set(self.$store.state, 'people', response.data); 
      //self.$store.commit('populate', response.data)
    }).catch(function (error) {
      console.dir(error);
    });
  },
  computed: {
    datadata: function() {
      return this.$store.state.people
    }
  },
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<script src="https://unpkg.com/vue-resource"></script>

<div id="app">
  Data: {{ datadata }}
</div>

the Vuex state reactive in the same way Vue data is (it converts the js object to an observable), or is it something else?

是的。实际上,正是 Vue 本身使商店对象具有反应性。来自Mutations official docs :

Mutations Follow Vue's Reactivity Rules

Since a Vuex store's state is made reactive by Vue, when we mutate the state, Vue components observing the state will update automatically. This also means Vuex mutations are subject to the same reactivity caveats when working with plain Vue:

  1. Prefer initializing your store's initial state with all desired fields upfront.

  2. When adding new properties to an Object, you should either:

  • Use Vue.set(obj, 'newProp', 123), or

  • Replace that Object with a fresh one. For example, using the stage-3 object spread syntax we can write it like this:

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

因此,即使在突变代码中,如果您直接覆盖可观察对象或创建新属性(通过不调用 Vue.set(obj, 'newProp', newValue)),对象也不会响应.


跟进评论中的问题(好的!)

So it seems the observable object is slightly different than the regular Vue data - changes are only allowed to happen from a mutation handler. Is that right?

他们可能是,但我不相信他们是。文档和证据(参见下面的 vm.$watch 讨论)指向它们与 data 对象完全相同,至少在 react /可观察行为方面。

How does the object "know" it was mutated from a different context?

这是个好问题。请允许我改写一下:

If calling Vue.set(object, 'prop', data); from within Vue throws an exception (see demo above), why calling Vue.set(object, 'prop', data); from within a mutation function doesn't?

答案在于within Store.commit()'s code .它执行突变代码 through a _withCommit() internal function .

所有这些this _withCommit() does is it sets a flag this._committing to true then 执行突变代码(并在执行后将 _committing 返回到 false)。

然后 Vuex store 是 watching the states' variables如果它注意到(又名观察者触发)variable changed while the _committing flag was false it throws the warning .

现在,为了证明我的观点,让我们通过将 state._committing 设置为 true“欺骗”vuex,然后调用 Vue.set() 来自增变器外部。正如您在下面看到的,没有警告被触发。图奇。

const store = new Vuex.Store({
strict: true,
  state: {
    people: []
  },
  mutations: {
    populate: function (state, data) {
      //Vue.set(state, 'people', data);
    }
  }
});
new Vue({
  store,
  el: '#app',
  mounted: function() {
    let self = this;
    this.$http.get('https://api.myjson.com/bins/g07qh').then(function (response) {
      // trick the store to think we're using commit()
      self.$store._committing = true;
      // setting without commit
      Vue.set(self.$store.state, 'people', response.data); 
      // no warning! yay!
    }).catch(function (error) {
      console.dir(error);
    });
  },
  computed: {
    datadata: function() {
      return this.$store.state.people
    }
  },
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<script src="https://unpkg.com/vue-resource"></script>

<div id="app">
  Data: {{ datadata }}
</div>

关于vue.js - vuex - 即使不推荐,是否可以直接更改状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49122648/

相关文章:

javascript - Vue.js2 - $emit 事件未被父组件捕获

vuex - 为什么更新数组中的对象在 VUE 中不起作用?

typescript - VSCode 和 Vue 2 插件 TypeScript 定义

javascript - 无法读取未定义的属性 'ref'

javascript - Object(...) 不是 Vuex Store 的函数

javascript - 使用 NuxtJS 和 Vuex 路由 404 - 组件应该调用突变还是操作可以返回 promise ?

arrays - Vue watch 数组(或对象)推送相同的旧值和新值

object - 如果 vue.js 2 上的对象为空,如何添加条件?

javascript - Vue中使用事件修饰符.prevent实现无重定向提交表单

vue.js - 将 vuejs 变量添加到 html 标签