【问题标题】:Vuex - Do not mutate vuex store state outside mutation handlersVuex - 不要在突变处理程序之外改变 vuex 存储状态
【发布时间】:2018-02-13 02:50:15
【问题描述】:

为什么会出现这个错误:

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

这是什么意思?

当我尝试输入编辑输入文件时会发生这种情况。

pages/todos/index.vue

<template>
  <ul>
    <li v-for="todo in todos">
      <input type="checkbox" :checked="todo.done" v-on:change="toggle(todo)">
      <span :class="{ done: todo.done }">{{ todo.text }}</span>
      <button class="destroy" v-on:click="remove(todo)">delete</button>

      <input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" @blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)" @keyup.esc="cancelEdit(todo)">

    </li>
    <li><input placeholder="What needs to be done?" autofocus v-model="todo" v-on:keyup.enter="add"></li>
  </ul>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  data () {
    return {
      todo: '',
      editedTodo: null
    }
  },
  head () {
    return {
      title: this.$route.params.slug || 'all',
      titleTemplate: 'Nuxt TodoMVC : %s todos'
    }
  },
  fetch ({ store }) {
    store.commit('todos/add', 'Hello World')
  },
  computed: {
    todos () {
      // console.log(this)
      return this.$store.state.todos.list
    }
  },
  methods: {
    add (e) {

      var value = this.todo && this.todo.trim()
      if (value) {
        this.$store.commit('todos/add', value)
        this.todo = ''
      }

    },
    toggle (todo) {
      this.$store.commit('todos/toggle', todo)
    },
    remove (todo) {
      this.$store.commit('todos/remove', todo)
    },

    doneEdit (todo) {
      this.editedTodo = null
      todo.text = todo.text.trim()
      if (!todo.text) {
        this.$store.commit('todos/remove', todo)
      }
    },
    cancelEdit (todo) {
      this.editedTodo = null
      todo.text = this.beforeEditCache
    },
  },
  directives: {
    'todo-focus' (el, binding) {
      if (binding.value) {
        el.focus()
      }
    }
  },
}
</script>

<style>
.done {
  text-decoration: line-through;
}
</style>

stores/todos.js

export const state = () => ({
  list: []
})

export const mutations = {
  add (state, text) {
    state.list.push({
      text: text,
      done: false
    })
  },
  remove (state, todo) {
    state.list.splice(state.list.indexOf(todo), 1)
  },
  toggle (state, todo) {
    todo.done = !todo.done
  }
}

有什么办法可以解决这个问题吗?

【问题讨论】:

  • 你可以关闭 Vuex 严格模式。
  • @Xhua 严格模式在这里是有原因的(更好的做法),而不是让它有更清晰的代码。

标签: vue.js vuex nuxt.js


【解决方案1】:

这个错误可能来自你shallow cloned an object
这意味着您尝试复制对象但对象不是原始类型(如StringNumber),因此它通过引用而不是值传递。
在这里,您认为您将一个对象克隆到另一个对象中,而您仍在引用旧对象。由于您正在改变旧版本,因此您收到了这个很好的警告。

这是来自Vue3's documentation 的 GIF(在我们的案例中仍然相关)。
在左侧,它显示了一个对象(杯子)未被正确克隆>>通过引用传递。
在右边,它被正确克隆 >> 按值传递。改变这个不会改变原来的


管理此错误的正确方法是使用lodash,这是在 Nuxt 中有效地加载它的方法:

  • 安装lodash-es,例如:yarn add lodash-es,这是一个优化的tree-shakable lodash ES module
  • 您可能还需要在您的nuxt.config.js 中使用以下代码进行转换
build: {
  transpile: ['lodash-es'],
}
  • 像这样将它加载到您的.vue 组件中
<script>
import { cloneDeep } from 'lodash-es'

...
const properlyClonedObject = cloneDeep(myDeeplyNestedObject)
...
</script>

几个关键点:

  • 建议使用 lodash 而不是 JSON.parse(JSON.stringify(object)),因为它确实可以处理 some edge-cases
  • 由于这种设置,我们只从 lodash 加载小函数,而不是整个库,因此在性能方面没有损失
  • lodash 有很多久经考验的有用功能,这是 JS 中严重缺乏的(没有核心库)

【讨论】:

    【解决方案2】:
    export default new Vuex.Store({
        ...
        strict: true
    })
    

    尝试评论“严格”

    【讨论】:

      【解决方案3】:

      以防万一有人还在为此烦恼, 我通过复制/克隆商店状态使我的代码正常工作。

      在你的情况下,尝试这样的事情......

      computed: {
        todos () {
          return [ ...this.$store.state.todos.list ]
        }
      }
      

      它基本上是一个扩展运算符,它导致复制 todos.list 数组。这样,您就不会直接更改状态的值,只是不要忘记提交,这样您的突变将保存在存储中。

      【讨论】:

      【解决方案4】:

      您好,我遇到了同样的问题,并使用以下方法之一克隆我的对象来解决它:

      { ...obj} //spread syntax 
      Object.assign({}, obj)
      JSON.parse(JSON.stringify(obj))
      

      对于您的代码,我认为您需要替换这部分

      computed: {
        todos () {
          // console.log(this)
          return this.$store.state.todos.list
        }
      }
      

      有了这个

      computed: {
        todos () {
          // console.log(this)
          return {...this.$store.state.todos.list}
        }
      }
      

      我不确定这是否是最好的方法,但希望这对遇到相同问题的其他人有所帮助。

      【讨论】:

      • OK 显示,但要重新设置值,仍然需要使每个字段突变(设置新值)
      【解决方案5】:

      在一个属于 Vuex 的状态上使用 v-model 可能有点棘手。

      你在todo.text上使用了v-model

      <input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" @blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)" @keyup.esc="cancelEdit(todo)">
      

      使用:value 读取值并使用v-on:inputv-on:change 执行在显式Vuex 突变处理程序中执行突变的方法

      这个问题在这里处理:https://vuex.vuejs.org/en/forms.html

      【讨论】:

      【解决方案6】:

      能用lodash就不用头疼了

      computed: {
        ...mapState({
          todo: (state) => _.cloneDeep(state.todo)
        })
      }
      

      【讨论】:

        猜你喜欢
        • 2019-09-24
        • 2022-01-09
        • 1970-01-01
        • 2020-03-30
        • 2023-03-09
        • 2016-11-16
        • 1970-01-01
        • 1970-01-01
        • 2019-11-30
        相关资源
        最近更新 更多