Vue过滤器和“不要在突变处理程序之外改变vuex存储状态”

问题描述 投票:0回答:2

我还在学习Vue,我无法理解为什么会出现这种错误。

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

这是我的代码:

const DATE_FORMAT = "DD-MM-YYYY";

Vue.filter("formatDate", function(value, dateFormat) {
  dateFormat = dateFormat ? dateFormat : DATE_FORMAT;
  if (value) {
    return moment(String(value)).format(dateFormat);
  }
});

Vue.component('list-component', {
  props: ['projects'],
  template: 
`
<div>
  <ul v-for="project in projects" :key="project.id">
    <li v-for="(value, key) in project" :key="key">
      {{ key }}: {{ value }}
    </li>
    <hr />
  </ul>
</div>
`
});

const store = new Vuex.Store({
  state: {
    projects: [{
      id: 1,
      name: "super project",
      startDate: "2014-09-01T08:18:28+02:00"
    }, {
      id: 2,
      name: "extra project",
      startDate: "2017-12-20T07:28:23.133+01:00"
    }, {
      id: 3,
      name: "small project",
      startDate: "2017-12-20T07:28:23.133+01:00"
    }]
  },
  getters: {
    allProjects: state => state.projects
  },
  strict: true
});

new Vue({
  el: '#app',
  computed: {
    formattedProjects() {
      var project = store.getters.allProjects;
      project.forEach(project => {
        project.startDate = this.$options.filters.formatDate(project.startDate);
      });
      return project;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vuex.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <list-component :projects="formattedProjects"></list-component>
</div>

https://jsfiddle.net/9sn78n1h/2/

我无法在组件中使用我的过滤器,因为属性名称可能不同。在上面的例子中它是startDate,但在其他数据中它可能是addDateendDate等。在vuex商店我需要格式原样的startDate,我不想改变它。我不确定在像allProjectsWithFormatedDate这样的vuex中写另一个getter是一个很好的做法,但即便如此,它仍然会对变异商店大吼大叫。

所以我在我的计算方法project中创建了一个局部变量formattedProjects(),我不知道为什么在更改它的值后我有这个错误。

有人可以帮我理解它并展示解决这个问题的正确方法吗?

我知道我可以将strict模式设置为false来静音这个错误(我应该在生产中),但我相信有更好的方法来解决这个问题。

vue.js vuejs2 vue-component vuex vue-filter
2个回答
1
投票

问题是错误消息所说的内容。 Vuex具有单向流,您必须注意这一点。对状态的任何更改都只能在突变部分进行。所以我们错误地改变了下面一行的对象。

project.startDate = this.$options.filters.formatDate(project.startDate);

这将在商店项目project上添加/更新(mutate)startDate属性。这是问题的根本原因。我认为Vuex必须有更好的方法来处理这个问题。如果你想在你的计算属性本身处理它,它可以这样做。

formattedProjects() {
  var clonedProject = []
  var project = store.getters.allProjects;
  project.forEach(project => {
    clonedProject.push({
      ...project,
      startData: this.$options.filters.formatDate(project.startDate)
    })
  });
  return clonedProject;
}

这是fiddle的链接


1
投票

很简单,因为你在不使用dispatchmutations的情况下直接修改状态。

有关更多信息,请参阅this页面。

在上面的示例中,您可以添加以下内容:

mutations: {
  updateProject (state, obj) {
    // Use state, get the project then set startDate.
    // You can use obj.projectId and obj.startDate
  }
}

然后在循环中进行更改:

this.$store.commit('updateProject', {
  projectId: project.id, 
  startDate: this.$options.filters.formatDate(project.startDate)
})

如果你添加了你的Vue实例,它将允许你在没有警告的情况下更新状态。

根据州的规模和应用程序的复杂程度,您可以考虑使用modules

与您的评论一致,如果您不尝试更新商店,只需在新对象中返回数据:

formattedProjects() {
  var project = store.getters.allProjects;
  var clone = []
  project.forEach(project => {
    clone.push({
      ...project,
      startData: this.$options.filters.formatDate(project.startDate)
    })
  });
  return clone;
}

这将返回一个新对象,而不是改变商店值。可能有一种更短的方式来返回数据,但是当我测试它时使用.map会导致问题,但你应该能够得到这个想法......

注意:另一个想法是遍历组件上的状态,只是在组件HTML中进行过滤 - 不确定为什么需要使用计算的prop。

你是更新的例子:

const DATE_FORMAT = "DD-MM-YYYY";

Vue.filter("formatDate", function(value, dateFormat) {
  dateFormat = dateFormat ? dateFormat : DATE_FORMAT;
  if (value) {
    return moment(String(value)).format(dateFormat);
  }
});

Vue.component('list-component', {
  props: ['projects'],
  template: 
`
<div>
  <ul v-for="project in projects" :key="project.id">
    <li v-for="(value, key) in project" :key="key">
      {{ key }}: {{ value }}
    </li>
    <hr />
  </ul>
</div>
`
});

const store = new Vuex.Store({
  state: {
    projects: [{
      id: 1,
      name: "super project",
      startDate: "2014-09-01T08:18:28+02:00"
    }, {
      id: 2,
      name: "extra project",
      startDate: "2017-12-20T07:28:23.133+01:00"
    }, {
      id: 3,
      name: "small project",
      startDate: "2017-12-20T07:28:23.133+01:00"
    }]
  },
  getters: {
    allProjects: state => state.projects
  },
  strict: true
});

new Vue({
  el: '#app',
  computed: {
    formattedProjects() {
      var project = store.getters.allProjects;
      var clone = []
      project.forEach(project => {
        clone.push({
          ...project,
          startData: this.$options.filters.formatDate(project.startDate)
        })
      });
      return clone;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vuex.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <list-component :projects="formattedProjects"></list-component>
</div>
© www.soinside.com 2019 - 2024. All rights reserved.