vuex - 是否有可能直接改变状态,即使不推荐?

时间:2018-03-06 02:49:13

标签: vue.js vuex

https://vuex.vuejs.org/en/getting-started.html处的文档 说,

  

您不能直接改变商店的状态。改变商店状态的唯一方法是明确提交突变。

我的问题是,这是一种好的做法,还是Vuex国家的内部工作方式如何?换句话说,Vuex状态是否以与Vue数据相同的方式反应(它将js对象转换为可观察的对象),还是其他东西?

类似的问题 - 您是否可以直接更改操作中的状态而不是创建突变?我知道这是不好的做法,它会失去遵循惯例的一些可追溯性 - 但是它有效吗?

2 个答案:

答案 0 :(得分:10)

  

您可以直接更改操作中的状态而不是创建突变吗?我知道这是不好的做法,它会失去遵循惯例的一些可追溯性 - 但是它有效吗?

工作,但会发出警告并发出错误。

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>

  

Vuex状态以与Vue数据相同的方式反应(它将js对象转换为可观察的对象),还是其他东西?

是。实际上,这是Vue本身使商店对象反应。来自Mutations official docs

  

突变遵循Vue的反应性规则

     

由于Vuex商店的状态被Vue反应,当我们改变时   状态,观察状态的Vue组件将自动更新。   这也意味着Vuex突变具有相同的反应性   使用普通Vue时需要注意:

     
      
  1. 首选初始化商店的初始状态,包括所有需要的字段。

  2.   
  3. 向Object添加新属性时,您应该:

         
        
    • 使用Vue.set(obj, 'newProp', 123)

    •   
    • 用新的Object替换该Object。例如,我们可以使用阶段3 object spread syntax   这样写:

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

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


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

  

因此,可观察对象似乎与常规Vue数据略有不同 - 只允许从变异处理程序进行更改。是吗?

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

  

对象如何“知道”它是从不同的上下文中变异的?

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

  

如果从Vue中调用Vue.set(object, 'prop', data);会引发异常(参见上面的演示),为什么在变异函数中调用Vue.set(object, 'prop', data);呢?

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

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

Vuex商店然后是watching the states' variables,如果它通知(也就是观察者触发)variable changed while the _committing flag was false it throws the warning

(Bonus:do notice that vuex uses vm.$watch - 如果您不熟悉,请查看Vue's vm.$watch API docs - 观察变量,另一个暗示状态对象与数据对象相同 - 它们依赖于Vue的内部结构。)

现在,为了证明我的观点,让我们自己将state._committing设置为true“欺骗”vuex,然后从mutator外部调用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>

答案 1 :(得分:3)

我将使其变得非常简单:

因为状态对象已经是reactive,所以您可以完全避免使用获取器和变量。 Vue的所有模板(包括计算,监视等)将继续像使用组件数据一样工作。商店的state充当shared data object

但是,这样做将失去实现时间旅行调试,撤消/重做和设置断点的能力,因为您将通过使用方法来规避command design pattern和成员的封装。 https://en.m.wikipedia.org/wiki/Command_pattern