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

时间:2017-12-27 12:21:37

标签: vue.js vuejs2 vue-component vuex vue-filter

我还在学习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;
    }
  }
});
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.js"></script>
<script src="https://unpkg.com/vuex@3.0.1/dist/vuex.js"></script>
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<div id="app">
  <list-component :projects="formattedProjects"></list-component>
</div>
&#13;
&#13;
&#13;

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

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

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

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

我知道我可以将strict模式设置为false以隐藏此错误(我应该在制作中),但我相信有更好的方法可以解决此问题。

2 个答案:

答案 0 :(得分: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;
}

这将返回 new 对象,而不会改变商店值。可能有一种更短的方式来返回数据,但在我测试时使用.map会导致问题但你应该能够理解......

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

您已更新示例:

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/vuex@3.0.1/dist/vuex.js"></script>
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<div id="app">
  <list-component :projects="formattedProjects"></list-component>
</div>

答案 1 :(得分: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

的链接