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

lau*_*kok 10 vue.js vuex nuxt.js

为什么我会收到此错误:

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

这是什么意思?

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

页/待办事项/ 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>
Run Code Online (Sandbox Code Playgroud)

商店/ 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
  }
}
Run Code Online (Sandbox Code Playgroud)

我有什么想法可以解决这个问题?

Bad*_*bet 16

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

v-modeltodo.text这里使用过:

<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)">
Run Code Online (Sandbox Code Playgroud)

用于:value读取值和/ v-on:inputv-on:change执行在显式Vuex变异处理程序内执行变异的方法

此问题在此处理:https://vuex.vuejs.org/en/forms.html

  • 谢谢。我读过那个。但是这个例子使用`v-model`很好 - https://github.com/nuxt/todomvc/blob/master/pages/_slug.vue? (2认同)

kis*_*ssu 14

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

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


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

  • 安装lodash-es,例如:yarn add lodash-es,这是一个优化的tree-shakable lodash ES模块
  • 您可能还需要nuxt.config.js使用以下内容将其转译为您的
build: {
  transpile: ['lodash-es'],
}
Run Code Online (Sandbox Code Playgroud)
  • .vue像这样将其加载到您的组件中
<script>
import { cloneDeep } from 'lodash-es'

...
const properlyClonedObject = cloneDeep(myDeeplyNestedObject)
...
</script>
Run Code Online (Sandbox Code Playgroud)

几个关键点:

  • 推荐使用 lodash,JSON.parse(JSON.stringify(object))因为它确实可以处理一些边缘情况
  • 由于这种设置,我们只从 lodash 加载小函数,而不是整个库,因此在性能方面没有任何损失
  • lodash有很多久经考验的有用功能,这是JS严重缺乏的(无核心库)
  • 更新structuredClone如果您正在寻找深度复制的解决方案,那么它也是原生的并且性能相当好,完全绕过了 Lodash 的需要。


小智 10

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

{ ...obj} //spread syntax 
Object.assign({}, obj)
JSON.parse(JSON.stringify(obj))
Run Code Online (Sandbox Code Playgroud)

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

  computed: {
    todos () {
      // console.log(this)
      return this.$store.state.todos.list
    }
  }
Run Code Online (Sandbox Code Playgroud)

有了这个

  computed: {
    todos () {
      // console.log(this)
      return {...this.$store.state.todos.list}
    }
  }
Run Code Online (Sandbox Code Playgroud)

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


Ane*_*eed 7

会用lodash就不头痛了

computed: {
  ...mapState({
    todo: (state) => _.cloneDeep(state.todo)
  })
}
Run Code Online (Sandbox Code Playgroud)


小智 5

以防万一有人仍然对此感到困扰,我通过复制/克隆商店状态来让我的代码正常工作。

对于你的情况,尝试这样的事情......

computed: {
  todos () {
    return [ ...this.$store.state.todos.list ]
  }
}
Run Code Online (Sandbox Code Playgroud)

它基本上是一个扩展运算符,它会生成 todos.list 数组的克隆。这样,您就不会直接更改状态的值,只是不要忘记提交,以便您的突变将保存在商店中。