Vuex 和 VueJS(不要在突变处理程序之外改变 vuex 存储状态)

Posted

技术标签:

【中文标题】Vuex 和 VueJS(不要在突变处理程序之外改变 vuex 存储状态)【英文标题】:Vuex & VueJS (Do not mutate vuex store state outside mutation handlers) 【发布时间】:2016-11-16 19:58:10 【问题描述】:

我正在尝试创建一个 listenAuth 函数来监视 firebase 中的“onAuthStateChanged”以通知 vuex 商店 当用户登录或退出时。据我所知,我只是在使用突变处理程序修改 state.authData,除非我遗漏了什么?

我收到错误消息:

[vuex] Do not mutate vuex store state outside mutation handlers.

这是我的 App.vue javascript(来自我的组件)

<script>
// import Navigation from './components/Navigation'
import * as actions from './vuex/actions'
import store from './vuex/store'
import firebase from 'firebase/app'

export default 
  store,
  ready: function () 
    this.listenAuth()
  ,
  vuex: 
    actions,
    getters: 
      authData: state => state.authData,
      user: state => state.user
    
  ,
  components: 
    // Navigation
  ,
  watch: 
    authData (val) 
      if (!val) 
        this.redirectLogin
        this.$route.router.go('/login')
      
    
  ,
  methods: 
    listenAuth: function () 
      firebase.auth().onAuthStateChanged((authData) => 
        this.changeAuth(authData)
      )
    
  

</script>

这是我的操作(changeAuth)函数

export const changeAuth = ( dispatch, state , authData) => 
  dispatch(types.AUTH_CHANGED, authData)

这是我的商店(重要的部分)

const mutations = 
  AUTH_CHANGED (state, authData) 
    state.authData = authData
  


const state = 
  authData: 

【问题讨论】:

你可能想升级到 Vuex 2.0 【参考方案1】:

我也遇到过这个问题。我的商店:

  state: 
    items: []
  ,
  mutations: 
    SetItems (state, payload) 
      // Warning
      state.items = payload.items
    
  ,
  actions: 
    FetchItems (commit, state, payload) 
      api.getItemsData(payload.sheetID)
        .then(items => commit('SetItems', items))
    
  

state.items = payload.items 替换为:

state.items = payload.items.slice()

原因是数组作为引用存储在 javascript 中,payload.items 很可能在 Vuex 之外被更改。所以我们应该 只需使用 payload.items 的新副本即可。

对于状态对象,使用:

state.someObj = Object.assign(, payload.someObj)

并且不要使用JSON.parse(JSON.stringify(someObj)),因为它要慢得多。

【讨论】:

【参考方案2】:

在解决了同样的问题后,我发现只有当我们尝试将 auth/user 数据存储在 Vuex 状态时才会发生错误。

改变...

const mutations = 
  AUTH_CHANGED (state, authData) 
    state.authData = authData
  

...到...

const mutations = 
  AUTH_CHANGED (state, authData) 
    state.authData = JSON.parse(JSON.stringify(authData))
  

会解决你的问题。

【讨论】:

请改用state.authData = Object.assign(, authData)。看看我下面的答案。 由于某种原因 Object.assign(, authData) 不起作用,但 JSON.parse(JSON.stringify(authData)) 起作用。 Object.assign 有时不起作用的原因是因为它不执行深层复制,如文档 "For deep cloning, we need to use other alternatives because Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value." 中所述。对于深度克隆,JSON.parse(JSON.stringify(obj)) 更好,即使它肯定会更慢 赞成建议的替代方案,也适用于我的身份验证 我想问为什么这个解决方案有效,因为没有解释。【参考方案3】:

我也有这个问题,我用lodash克隆数据

state.someStatehere = $lodash.cloneDeep(data)

【讨论】:

【参考方案4】:

所以从许多答案中我们可以就原因达成一致:存储在状态中的对象被持有对它的引用的东西默默地修改。许多人建议制作对象的副本。

另一种选择是在修改对象时清空状态。当由于某种原因您无法复制对象时,此方法有效。 例如:

bla( state  ) 
  mutatingMethod(state.xxx);
,

如果您知道mutatingMethod 正在修改 xxx,那么您可以这样做:

bla( state  ) 
  let xxx = state.xxx
  commit("SET_XXX", null)
  mutatingMethod(xxx);
  commit("SET_XXX", xxx)
,

【讨论】:

【参考方案5】:

对于同样为此苦苦挣扎的任何人,这是解决它的另一种方法(实际上对我有用):

auth()
  .onAuthStateChanged((user) => 
    if (user) 
      commit(MUTATION_TYPES.SET_USER,  ...user.toJSON() );
    
  )

【讨论】:

以上是关于Vuex 和 VueJS(不要在突变处理程序之外改变 vuex 存储状态)的主要内容,如果未能解决你的问题,请参考以下文章

Vuex 突变引发错误 - 不要在突变处理程序之外改变 vuex 存储状态

nuxt vuex:不要在突变处理程序之外改变 vuex 存储状态

错误:[vuex] 不要在突变处理程序之外改变 vuex 存储状态。 NUXT

错误:[vuex] 不要在突变处理程序之外改变 vuex 存储状态

vuex 错误 - 不要在突变处理程序之外改变 vuex 存储状态

不要在突变处理程序之外改变 vuex 存储状态