vuex - 是否可以直接更改状态,即使不推荐? [英] vuex - is it possible to directly change state, even if not recommended?

查看:34
本文介绍了vuex - 是否可以直接更改状态,即使不推荐?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

文档这里说,

<块引用>

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

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

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

解决方案

你能直接在一个动作中改变状态而不是创建一个突变吗?我知道这是不好的做法,它失去了遵循惯例所提供的一些可追溯性 - 但它有效吗?

有效,但会引发警告和错误.

vue.js:584 [Vue 警告]:观察者回调错误function () { return this._data.$$state }":错误:[vuex] 不要在外面改变 vuex 存储状态突变处理程序."(在<组件>中找到)警告@vue.js:584...vue.js:1719 错误:[vuex] 不要在变异处理程序之外改变 vuex 存储状态.在断言 (VM260 vuex.js:103)

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

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

const store = new Vuex.Store({严格:真实,状态: {人们: []},突变:{填充:函数(状态,数据){//Vue.set(state, 'people', data);}}});新的 Vue({店铺,el: '#app',安装:功能(){让 self = this;this.$http.get('https://api.myjson.com/bins/g07qh').then(function (response) {//设置不提交Vue.set(self.$store.state, 'people', response.data);//self.$store.commit('populate', response.data)}).catch(函数(错误){控制台目录(错误);});},计算:{数据数据:函数(){返回 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="应用程序">数据:{{数据数据}}

<块引用>

Vuex 状态的响应方式与 Vue 数据相同(它将 js 对象转换为 observable),还是其他什么?

是的.实际上,正是 Vue 本身使 store 对象具有反应性.来自 Mutations 官方文档:

<块引用>

变异遵循 Vue 的反应性规则

由于 Vuex 存储的状态是由 Vue 响应的,所以当我们改变状态,观察状态的 Vue 组件会自动更新.这也意味着 Vuex 突变受到相同的反应性使用普通 Vue 时的注意事项:

  1. 优先使用所有需要的字段预先初始化商店的初始状态.

  2. 向对象添加新属性时,您应该:

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

    • 用一个新的对象替换那个对象.例如,使用 stage-3 对象传播语法我们可以写成这样:

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

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


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

<块引用>

因此,observable 对象似乎与常规 Vue 数据略有不同 - 更改仅允许从突变处理程序发生.是吗?

他们可能是,但我不相信他们是.文档和证据(请参阅下面的 vm.$watch 讨论)表明它们与 data 对象完全相同,至少在反应/可观察行为方面是这样.

<块引用>

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

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

<块引用>

如果从 Vue 内部调用 Vue.set(object, 'prop', data); 抛出异常(见上面的演示),为什么调用 Vue.set(object, 'prop', data); 来自变异函数内的不是吗?

答案在Store.commit() 的代码.它执行变异代码 通过一个<代码> 内部函数.

所有这些这个_with/code> 的作用是将标志 this._committing 设置为 true 并且 then 执行变异代码(并返回 _committingfalse 执行后).

Vuex 商店然后 观察状态变量a> 并且如果它注意到(又名观察者触发)_committing 标志为 false 时发生变化,它会抛出警告.

(奖励:请注意 vuex 使用 .$watch --see Vue 的 vm.$watch API 文档 如果你不熟悉它——观察变量,另一个提示状态对象与数据对象相同——它们依赖于 Vue 的内部.)

现在,为了证明我的观点,让我们通过将 state._committing 设置为 true欺骗"vuex,然后调用 Vue.set() 来自 mutator 之外.如下所示,没有警告被触发.触摸.

const store = new Vuex.Store({严格:真实,状态: {人们: []},突变:{填充:函数(状态,数据){//Vue.set(state, 'people', data);}}});新的 Vue({店铺,el: '#app',安装:功能(){让 self = this;this.$http.get('https://api.myjson.com/bins/g07qh').then(function (response) {//欺骗商店认为我们正在使用 commit()self.$store._committing = true;//设置不提交Vue.set(self.$store.state, 'people', response.data);//没有警告!好极了!}).catch(函数(错误){控制台目录(错误);});},计算:{数据数据:函数(){返回 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="应用程序">数据:{{数据数据}}

The documentation here says,

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

My question is, is that good practice, or that's how the internals of the Vuex state work? In other words, is the Vuex state reactive in the same way Vue data is (it converts the JS object to an observable), or is it something else?

A similar question - 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?

解决方案

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?

Works, but throws a warning AND an error.

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)

who knows what else might be broken after this.

See for yourself (notice the data updates in the template):

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?

Yes. Actually, that's Vue itself that makes the store objects reactive. From the 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 }
      

So even within mutations code, if you overwrite observables or create new properties directly (by not calling Vue.set(obj, 'newProp', newValue)), the object won't be reactive.


Follow up questions from comments (good ones!)

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?

They could be, but I don't believe they are. The docs and evidences (see below vm.$watch discussion below) point torward they being exactly the same as data objects, at least with regards to reaction/observable behaviors.

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

This is a good question. Allow me to rephrase it:

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?

The answer lies within Store.commit()'s code. It executes the mutation code through a _withCommit() internal function.

All that this _withCommit() does is it sets a flag this._committing to true and then executes the mutation code (and returns _committing to false after the exection).

The Vuex store is then watching the states' variables and if it notices (aka the watcher triggers) that the variable changed while the _committing flag was false it throws the warning.

(Bonus: do notice that vuex uses vm.$watch --see Vue's vm.$watch API docs if you are not familiar with it -- to observe the variables, another hint that state's objects are the same as data objects - they rely on Vue's internals.)

Now, to prove my point, let's "trick" vuex by setting state._committing to true ourselves and then call Vue.set() from outside a mutator. As you can see below, no warning is triggered. Touché.

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>

这篇关于vuex - 是否可以直接更改状态,即使不推荐?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆