Vue 过滤器和 "Do not mutate vuex store state outside mutation handlers"
Vue filter and the "Do not mutate vuex store state outside mutation handlers"
我还在学习Vue,我不明白为什么会出现这个错误。
Error: [vuex] Do not mutate vuex store state outside mutation handlers.
这是我的代码:
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/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>
https://jsfiddle.net/9sn78n1h/2/
我无法在组件中使用我的过滤器,因为 属性 名称可能不同。在上面的示例中它是 startDate
,但在其他数据中它可能是 addDate
、endDate
等。
在 vuex 商店中,我需要原样格式的 startDate
,我不想改变它。
我不确定像 allProjectsWithFormatedDate
这样在 vuex 中写另一个 getter 是一个好习惯,但即便如此,它仍然对 mutating store 大喊大叫。
所以我在我的计算方法 formattedProjects()
中创建了一个局部变量 project
,但我不知道为什么在更改它的值后会出现此错误。
有人可以帮助我理解它并展示解决这个问题的正确方法吗?
我知道我可以将 strict
模式设置为 false
来消除这个错误(我应该在生产中),但我相信有更好的方法来解决这个问题。
很简单,因为您直接修改状态而不使用 dispatch
或 mutations
。
有关详细信息,请参阅 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。
根据您的评论,如果您不尝试更新商店,只需 return 新对象中的数据:
formattedProjects() {
var project = store.getters.allProjects;
var clone = []
project.forEach(project => {
clone.push({
...project,
startData: this.$options.filters.formatDate(project.startDate)
})
});
return clone;
}
这将 return 一个 new 对象并且不会改变存储值。 return 数据可能有更短的方法,但在我测试时使用 .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>
问题出在错误消息中。 Vuex 有单向流,这点你必须要注意。对状态的任何更改都必须仅在突变部分进行。所以我们错误地改变了下面一行中的对象。
project.startDate = this.$options.filters.formatDate(project.startDate);
这将 add/update(改变)商店商品 project
的开始日期 属性。这是问题的根本原因。我认为 Vuex 必须有更好的方法来处理这个问题。如果你想在你的计算 属性 本身中处理它,它可以像这样完成。
formattedProjects() {
var clonedProject = []
var project = store.getters.allProjects;
project.forEach(project => {
clonedProject.push({
...project,
startData: this.$options.filters.formatDate(project.startDate)
})
});
return clonedProject;
}
这里是link到fiddle
我还在学习Vue,我不明白为什么会出现这个错误。
Error: [vuex] Do not mutate vuex store state outside mutation handlers.
这是我的代码:
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/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>
https://jsfiddle.net/9sn78n1h/2/
我无法在组件中使用我的过滤器,因为 属性 名称可能不同。在上面的示例中它是 startDate
,但在其他数据中它可能是 addDate
、endDate
等。
在 vuex 商店中,我需要原样格式的 startDate
,我不想改变它。
我不确定像 allProjectsWithFormatedDate
这样在 vuex 中写另一个 getter 是一个好习惯,但即便如此,它仍然对 mutating store 大喊大叫。
所以我在我的计算方法 formattedProjects()
中创建了一个局部变量 project
,但我不知道为什么在更改它的值后会出现此错误。
有人可以帮助我理解它并展示解决这个问题的正确方法吗?
我知道我可以将 strict
模式设置为 false
来消除这个错误(我应该在生产中),但我相信有更好的方法来解决这个问题。
很简单,因为您直接修改状态而不使用 dispatch
或 mutations
。
有关详细信息,请参阅 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。
根据您的评论,如果您不尝试更新商店,只需 return 新对象中的数据:
formattedProjects() {
var project = store.getters.allProjects;
var clone = []
project.forEach(project => {
clone.push({
...project,
startData: this.$options.filters.formatDate(project.startDate)
})
});
return clone;
}
这将 return 一个 new 对象并且不会改变存储值。 return 数据可能有更短的方法,但在我测试时使用 .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>
问题出在错误消息中。 Vuex 有单向流,这点你必须要注意。对状态的任何更改都必须仅在突变部分进行。所以我们错误地改变了下面一行中的对象。
project.startDate = this.$options.filters.formatDate(project.startDate);
这将 add/update(改变)商店商品 project
的开始日期 属性。这是问题的根本原因。我认为 Vuex 必须有更好的方法来处理这个问题。如果你想在你的计算 属性 本身中处理它,它可以像这样完成。
formattedProjects() {
var clonedProject = []
var project = store.getters.allProjects;
project.forEach(project => {
clonedProject.push({
...project,
startData: this.$options.filters.formatDate(project.startDate)
})
});
return clonedProject;
}
这里是link到fiddle