为什么我的 Vue 应用程序中的 v-model 输入会在更改时触发突变错误?
Why are v-model inputs in my Vue app triggering mutation errors on change?
我有一些带有本地数据对象的 V 模型输入:
- 如果我发送本地过滤器对象作为我的操作的有效负载,一切正常。
- 如果我首先将过滤器对象存储在 vuex 存储中,然后将其作为有效负载传递给我的操作,它也可以工作,但是如果我随后更改组件中的任何输入,我会收到 Vuex 突变错误。
为什么更改 v-model 输入会触发突变?如何将此组件过滤器对象存储在 vuex 存储中并将其用作我的过滤操作的有效负载?
<template>
<div class="q-pa-md q-mb-md bg-grey-2 rounded-borders">
<div class="row q-gutter-md items-start justify-start">
<q-select
:options="typeOptions"
dense
outlined
transition-hide="flip-down"
transition-show="flip-up"
v-model="filter.type"
style="min-width: 120px;"
/>
<q-input dense outlined v-model="filter.startLastSeenTime" mask="date">
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
<q-date v-model="filter.startLastSeenTime" @input="() => $refs.qDateProxy.hide()" />
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-input dense outlined v-model="filter.endLastSeenTime" mask="date">
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
<q-date v-model="filter.endLastSeenTime" @input="() => $refs.qDateProxy.hide()" />
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-input
dense
outlined
placeholder="Keyword search"
type="search"
v-model="filter.textSearch"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-input
dense
outlined
placeholder="Browser name"
v-model="filter.browser"
/>
<q-input
dense
outlined
placeholder="Device ID"
v-model="filter.deviceId"
/>
<q-input
dense
outlined
placeholder="Tracking ID"
v-model="filter.trackingId"
/>
<q-input
dense
outlined
placeholder="Fingerprint"
v-model="filter.fingerprint"
/>
<q-input
dense
outlined
placeholder="IP address"
v-model="filter.ipAddress"
/>
<q-input
dense
outlined
placeholder="Installation ID"
v-model="filter.installationId"
/>
<q-input
dense
outlined
placeholder="Hook name"
v-model="filter.hookName"
/>
<q-input
dense
outlined
placeholder="Channel ID"
v-model="filter.channelId"
/>
<q-checkbox
color="primary"
class="self-center"
dense
label="Forensics Active"
v-model="filter.forensicsActive"
/>
<q-space />
<q-btn @click="resetFilter();" label="reset" color="grey" />
<q-btn @click="filterDevices();" label="Filter" color="primary" class="q-px-md" />
</div>
</div>
</template>
<script>
export default {
name: 'Filters',
data() {
return {
typeOptions: [
'Android', 'iOS', 'Web',
],
filter: {
type: 'ANDROID',
page: 0,
forensicsActive: false,
textSearch: '',
browser: '',
deviceId: '',
trackingId: '',
fingerprint: '',
ipAddress: '',
installationId: '',
hookName: '',
channelId: '',
startLastSeenTime: '',
endLastSeenTime: '',
},
};
},
methods: {
resetFilter() {
Object.keys(this.filter).forEach((key) => {
this.filter[key] = '';
});
this.filter.forensicsActive = false;
this.filter.page = 0;
this.$store.commit('Devices/UPDATE_ACTIVE_FILTER', this.filter);
this.$store.dispatch('Devices/filterDevices', this.$store.getters['Devices/activeFilter']);
},
filterDevices() {
// this works also! but after the first time it runs any change to the inputs in this component throws a vuex mutation error
// this.$store.commit('Devices/UPDATE_ACTIVE_FILTER', this.filter);
// this.$store.dispatch('Devices/filterDevices', this.$store.getters['Devices/activeFilter']);
// this works without errors
this.$store.dispatch('Devices/filterDevices', this.filter);
},
},
};
</script>
状态:(Vuex 模块)
export default function () {
return {
activeFilter: {
browser: '',
channelId: '',
deviceId: '',
endLastSeenTime: '',
fingerprint: '',
forensicsActive: false,
hookName: '',
installationId: '',
ipAddress: '',
page: 0,
startLastSeenTime: '',
textSearch: '',
trackingId: '',
type: 'ANDROID',
},
devicesList: {},
deviceToView: [],
};
}
错误:
[Vue warn]: Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers."
(found in <Root>)
warn @ vue.runtime.esm.js?5593:619
logError @ vue.runtime.esm.js?5593:1884
globalHandleError @ vue.runtime.esm.js?5593:1879
handleError @ vue.runtime.esm.js?5593:1839
run @ vue.runtime.esm.js?5593:4570
update @ vue.runtime.esm.js?5593:4542
notify @ vue.runtime.esm.js?5593:730
reactiveSetter @ vue.runtime.esm.js?5593:1055
set @ vue.runtime.esm.js?5593:1077
callback @ Filters.vue?d4cd:25
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
toggleOption @ QSelect.js?9e66:449
click @ QSelect.js?9e66:282
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
__onClick @ QItem.js?fe67:97
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
original._wrapper @ vue.runtime.esm.js?5593:6917
vue.runtime.esm.js?5593:1888 Error: [vuex] do not mutate vuex store state outside mutation handlers.
at assert (vuex.esm.js?94e4:94)
at Vue.store._vm.$watch.deep (vuex.esm.js?94e4:834)
at Watcher.run (vue.runtime.esm.js?5593:4568)
at Watcher.update (vue.runtime.esm.js?5593:4542)
at Dep.notify (vue.runtime.esm.js?5593:730)
at Object.reactiveSetter [as type] (vue.runtime.esm.js?5593:1055)
at Proxy.set (vue.runtime.esm.js?5593:1077)
at callback (Filters.vue?d4cd:25)
at invokeWithErrorHandling (vue.runtime.esm.js?5593:1854)
at VueComponent.invoker (vue.runtime.esm.js?5593:2179)
logError @ vue.runtime.esm.js?5593:1888
globalHandleError @ vue.runtime.esm.js?5593:1879
handleError @ vue.runtime.esm.js?5593:1839
run @ vue.runtime.esm.js?5593:4570
update @ vue.runtime.esm.js?5593:4542
notify @ vue.runtime.esm.js?5593:730
reactiveSetter @ vue.runtime.esm.js?5593:1055
set @ vue.runtime.esm.js?5593:1077
callback @ Filters.vue?d4cd:25
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
toggleOption @ QSelect.js?9e66:449
click @ QSelect.js?9e66:282
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
__onClick @ QItem.js?fe67:97
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
original._wrapper @ vue.runtime.esm.js?5593:6917
我一整天都尝试了所有我能读到的内容,包括 https://vuex.vuejs.org/guide/forms.html 和 lodash 克隆,但我仍然不明白为什么在不启动我的方法的情况下更改输入会导致发生突变。
只有按照 in the doc.
中所述设置双向计算属性,才能对存储在 Vuex 中的数据使用 v-model
指令
更新
根据我从评论中了解到的情况,您应该将突变更改为如下内容:
export default {
// ...
state: {
activeFilter: {
browser: '',
channelId: '',
deviceId: '',
endLastSeenTime: '',
fingerprint: '',
forensicsActive: false,
hookName: '',
installationId: '',
ipAddress: '',
page: 0,
startLastSeenTime: '',
textSearch: '',
trackingId: '',
type: 'ANDROID',
}
},
mutations: {
// ...
UPDATE_ACTIVE_FILTER(state, payload) {
Object.keys(payload).forEach(filterKey => {
state.activeFilter[filterKey] = payload[filterKey];
});
}
}
}
您不应该直接在组件中使用突变。
这是您应该使用的,而不是 this.$state.commit
和 this.$state.dispatch
https://vuex.vuejs.org/guide/actions.html
https://vuex.vuejs.org/guide/actions.html#dispatching-actions-in-components
这是您应该使用的,而不是 this.$state.getters
https://vuex.vuejs.org/guide/getters.html
https://vuex.vuejs.org/guide/getters.html#the-mapgetters-helper
我有一些带有本地数据对象的 V 模型输入:
- 如果我发送本地过滤器对象作为我的操作的有效负载,一切正常。
- 如果我首先将过滤器对象存储在 vuex 存储中,然后将其作为有效负载传递给我的操作,它也可以工作,但是如果我随后更改组件中的任何输入,我会收到 Vuex 突变错误。
为什么更改 v-model 输入会触发突变?如何将此组件过滤器对象存储在 vuex 存储中并将其用作我的过滤操作的有效负载?
<template>
<div class="q-pa-md q-mb-md bg-grey-2 rounded-borders">
<div class="row q-gutter-md items-start justify-start">
<q-select
:options="typeOptions"
dense
outlined
transition-hide="flip-down"
transition-show="flip-up"
v-model="filter.type"
style="min-width: 120px;"
/>
<q-input dense outlined v-model="filter.startLastSeenTime" mask="date">
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
<q-date v-model="filter.startLastSeenTime" @input="() => $refs.qDateProxy.hide()" />
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-input dense outlined v-model="filter.endLastSeenTime" mask="date">
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
<q-date v-model="filter.endLastSeenTime" @input="() => $refs.qDateProxy.hide()" />
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-input
dense
outlined
placeholder="Keyword search"
type="search"
v-model="filter.textSearch"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-input
dense
outlined
placeholder="Browser name"
v-model="filter.browser"
/>
<q-input
dense
outlined
placeholder="Device ID"
v-model="filter.deviceId"
/>
<q-input
dense
outlined
placeholder="Tracking ID"
v-model="filter.trackingId"
/>
<q-input
dense
outlined
placeholder="Fingerprint"
v-model="filter.fingerprint"
/>
<q-input
dense
outlined
placeholder="IP address"
v-model="filter.ipAddress"
/>
<q-input
dense
outlined
placeholder="Installation ID"
v-model="filter.installationId"
/>
<q-input
dense
outlined
placeholder="Hook name"
v-model="filter.hookName"
/>
<q-input
dense
outlined
placeholder="Channel ID"
v-model="filter.channelId"
/>
<q-checkbox
color="primary"
class="self-center"
dense
label="Forensics Active"
v-model="filter.forensicsActive"
/>
<q-space />
<q-btn @click="resetFilter();" label="reset" color="grey" />
<q-btn @click="filterDevices();" label="Filter" color="primary" class="q-px-md" />
</div>
</div>
</template>
<script>
export default {
name: 'Filters',
data() {
return {
typeOptions: [
'Android', 'iOS', 'Web',
],
filter: {
type: 'ANDROID',
page: 0,
forensicsActive: false,
textSearch: '',
browser: '',
deviceId: '',
trackingId: '',
fingerprint: '',
ipAddress: '',
installationId: '',
hookName: '',
channelId: '',
startLastSeenTime: '',
endLastSeenTime: '',
},
};
},
methods: {
resetFilter() {
Object.keys(this.filter).forEach((key) => {
this.filter[key] = '';
});
this.filter.forensicsActive = false;
this.filter.page = 0;
this.$store.commit('Devices/UPDATE_ACTIVE_FILTER', this.filter);
this.$store.dispatch('Devices/filterDevices', this.$store.getters['Devices/activeFilter']);
},
filterDevices() {
// this works also! but after the first time it runs any change to the inputs in this component throws a vuex mutation error
// this.$store.commit('Devices/UPDATE_ACTIVE_FILTER', this.filter);
// this.$store.dispatch('Devices/filterDevices', this.$store.getters['Devices/activeFilter']);
// this works without errors
this.$store.dispatch('Devices/filterDevices', this.filter);
},
},
};
</script>
状态:(Vuex 模块)
export default function () {
return {
activeFilter: {
browser: '',
channelId: '',
deviceId: '',
endLastSeenTime: '',
fingerprint: '',
forensicsActive: false,
hookName: '',
installationId: '',
ipAddress: '',
page: 0,
startLastSeenTime: '',
textSearch: '',
trackingId: '',
type: 'ANDROID',
},
devicesList: {},
deviceToView: [],
};
}
错误:
[Vue warn]: Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers."
(found in <Root>)
warn @ vue.runtime.esm.js?5593:619
logError @ vue.runtime.esm.js?5593:1884
globalHandleError @ vue.runtime.esm.js?5593:1879
handleError @ vue.runtime.esm.js?5593:1839
run @ vue.runtime.esm.js?5593:4570
update @ vue.runtime.esm.js?5593:4542
notify @ vue.runtime.esm.js?5593:730
reactiveSetter @ vue.runtime.esm.js?5593:1055
set @ vue.runtime.esm.js?5593:1077
callback @ Filters.vue?d4cd:25
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
toggleOption @ QSelect.js?9e66:449
click @ QSelect.js?9e66:282
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
__onClick @ QItem.js?fe67:97
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
original._wrapper @ vue.runtime.esm.js?5593:6917
vue.runtime.esm.js?5593:1888 Error: [vuex] do not mutate vuex store state outside mutation handlers.
at assert (vuex.esm.js?94e4:94)
at Vue.store._vm.$watch.deep (vuex.esm.js?94e4:834)
at Watcher.run (vue.runtime.esm.js?5593:4568)
at Watcher.update (vue.runtime.esm.js?5593:4542)
at Dep.notify (vue.runtime.esm.js?5593:730)
at Object.reactiveSetter [as type] (vue.runtime.esm.js?5593:1055)
at Proxy.set (vue.runtime.esm.js?5593:1077)
at callback (Filters.vue?d4cd:25)
at invokeWithErrorHandling (vue.runtime.esm.js?5593:1854)
at VueComponent.invoker (vue.runtime.esm.js?5593:2179)
logError @ vue.runtime.esm.js?5593:1888
globalHandleError @ vue.runtime.esm.js?5593:1879
handleError @ vue.runtime.esm.js?5593:1839
run @ vue.runtime.esm.js?5593:4570
update @ vue.runtime.esm.js?5593:4542
notify @ vue.runtime.esm.js?5593:730
reactiveSetter @ vue.runtime.esm.js?5593:1055
set @ vue.runtime.esm.js?5593:1077
callback @ Filters.vue?d4cd:25
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
toggleOption @ QSelect.js?9e66:449
click @ QSelect.js?9e66:282
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
Vue.$emit @ vue.runtime.esm.js?5593:3888
Vue.<computed> @ backend.js:1793
__onClick @ QItem.js?fe67:97
invokeWithErrorHandling @ vue.runtime.esm.js?5593:1854
invoker @ vue.runtime.esm.js?5593:2179
original._wrapper @ vue.runtime.esm.js?5593:6917
我一整天都尝试了所有我能读到的内容,包括 https://vuex.vuejs.org/guide/forms.html 和 lodash 克隆,但我仍然不明白为什么在不启动我的方法的情况下更改输入会导致发生突变。
只有按照 in the doc.
中所述设置双向计算属性,才能对存储在 Vuex 中的数据使用v-model
指令
更新
根据我从评论中了解到的情况,您应该将突变更改为如下内容:
export default {
// ...
state: {
activeFilter: {
browser: '',
channelId: '',
deviceId: '',
endLastSeenTime: '',
fingerprint: '',
forensicsActive: false,
hookName: '',
installationId: '',
ipAddress: '',
page: 0,
startLastSeenTime: '',
textSearch: '',
trackingId: '',
type: 'ANDROID',
}
},
mutations: {
// ...
UPDATE_ACTIVE_FILTER(state, payload) {
Object.keys(payload).forEach(filterKey => {
state.activeFilter[filterKey] = payload[filterKey];
});
}
}
}
您不应该直接在组件中使用突变。
这是您应该使用的,而不是 this.$state.commit
和 this.$state.dispatch
https://vuex.vuejs.org/guide/actions.html
https://vuex.vuejs.org/guide/actions.html#dispatching-actions-in-components
这是您应该使用的,而不是 this.$state.getters
https://vuex.vuejs.org/guide/getters.html
https://vuex.vuejs.org/guide/getters.html#the-mapgetters-helper