如何在 Vuex 突变中设置参数和参数
How to set up parameters and arguments in Vuex mutations
我正在使用 Vue.js、Vuex 和 Firebase 构建一个待办事项列表应用程序。该应用程序似乎可以正常工作,因为 Store 文件成功地管理了输入的待办事项(进出 firestore)的检索和呈现。但是,我对在 Vuex 中设置参数仍然有疑问。突变中的 REMOVE_TODO 函数(参见 store.js)似乎需要两个参数,即使 "id" 是函数实际代码中引用的唯一参数。换句话说,如果我取出初始参数(在本例中为 "state"),那么控制台将 return 一个错误,内容为:"Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: a custom Object object"。我的问题是:如果 "id" 参数是函数中实际使用的唯一参数,为什么这个 REMOVE_TODO 函数需要有两个参数才能正常运行?为什么需要另一个参数?下面是我的代码。谢谢!
app.vue
<template>
<div id="app" class="container">
<input class="form-control" :value="newTodo" @change="getTodo" placeholder="I need to...">
<button class="btn btn-primary" @click="addTodo">Add New Post</button>
<ul class="list-group">
<li class="list-group-item" v-for="todo in this.$store.getters.getTodos" :key="todo.id">
{{todo.title}}
<div class="btn-group">
<button type="button" @click="remove(todo.id)" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-remove-circle"></span> Remove
</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
beforeCreate: function() {
this.$store.dispatch('setTodo')
},
methods: {
getTodo(event) {
this.$store.dispatch('getTodo', event.target.value)
},
addTodo() {
this.$store.dispatch('addTodo')
this.$store.dispatch('clearTodo')
},
remove(id){
this.$store.dispatch('removeTodo', id)
}
},
computed: {
newTodo() {
return this.$store.getters.newTodo
},
todos(){
return this.$store.getters.todos
}
}
}
</script>
<style>
body {
font-family: Helvetica, sans-serif;
}
li {
margin: 10px;
}
</style>
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import db from '../firebase'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
todos: [],
newTodo: '',
errors: ''
},
mutations: { //syncronous, committed
GET_TODO: (state, todo) => {
state.newTodo = todo
},
ADD_TODO: state => {
db.collection('items').add({
title: state.newTodo,
created_at: Date.now(),
}).then(function(){
console.log('Document successfully added')
})
.catch((error) => {
this.errors = error
})
},
REMOVE_TODO: (state, id) => {
if (id) {
db.collection("items").doc(id).delete().then(function() {
console.log('Document successfully deleted')
})
.catch((error) => {
this.errors = error
})
} else {
this.errors = 'Invalid ID'
}
},
CLEAR_TODO: state => {
state.newTodo = ''
},
SET_TODO: state => {
let todos = []
db.collection('items').orderBy('created_at').onSnapshot((snapshot) => {
todos = []
snapshot.forEach((doc) => {
todos.push({ id: doc.id, title: doc.data().title })
})
state.todos = todos
})
}
},
actions: { //asyncronous, dispatched
getTodo: (context, todo) => {
context.commit('GET_TODO', todo)
},
addTodo: context => {
context.commit('ADD_TODO')
},
removeTodo: (context, id) => {
context.commit('REMOVE_TODO', id)
},
clearTodo: context => {
context.commit('CLEAR_TODO')
},
setTodo: context => {
context.commit('SET_TODO')
}
},
getters: {
newTodo: state => state.newTodo,
getTodos: state => {
return state.todos
}
}
})
这与 Vuex 没有任何关系,只是 JavaScript 函数调用的工作方式。参数按位置而不是名称传递。
你有 REMOVE_TODO: (state, id) => {
但名称 state
和 id
只有在函数内部才真正有意义。在函数之外,从调用者的角度来看,这些名称并不重要。它可以很容易地成为 REMOVE_TODO: (a, b) => {
.
当 Vuex 调用 mutation 时,它将传递 state
对象作为第一个参数,payload
作为第二个参数。实际上它在调用:
mutations.REMOVE_TODO(state, payload)
我重申,参数的名称实际上并不重要,重要的是它们的位置。
这是 Vuex 内部的,不是你可以直接控制的。如果您自己调用该函数,则可以传递任何您想要的东西,但您没有(也不应该)。相反,您(非常正确地)调用 commit
。您将所需的 payload
传递给 commit
,然后 Vuex 将完成剩下的工作,调用相关的突变并将其传递给 state
和 payload
.
将函数定义为 REMOVE_TODO: (id) => {
不会更改调用方传递的参数。第一个参数仍然是 state
对象。您已将其命名为 id
但这没有任何区别,它仍然是 state
对象。
一般来说,尝试删除第一个参数的问题是所有其他参数有效地向下排列,因此所需参数名称的位置不再与传递的参数的位置匹配。从最后删除一个参数没有这样的问题,因为其他参数不会移动位置。所以如果你只需要 state
但不需要 payload
那么你可以只删除第二个参数没问题。 API 设计者总是尝试将可选参数放在末尾,这样它们可以被删除而不会导致任何问题。
对于突变,payload
很容易是不必要的,但 state
总是需要的。突变的全部意义在于改变 state
。如果你不改变 state
那么你不应该使用突变。
如您的代码中所述,actions
是异步的,而 mutations
需要是同步的。但这不是你所拥有的。目前,您的突变中有很多对 Firebase 的异步调用。所有这些都需要移入 actions
。请注意,Promises 始终是异步的,因此如果您发现自己在突变中调用 then
,那您就错了。唯一应该在 mutations
内的部分是当您修改 state
.
时
您还需要避免在 Vuex 存储中使用 this
。你应该发现你需要的一切都可以从传递给函数的参数中访问。目前你有 this.errors = error
和类似的突变。我假设应该是 state.errors = error
.
更新:
根据评论中的要求:
SET_TODO (state, todos) {
state.todos = todos
}
setTodo ({commit}) {
db.collection('items').orderBy('created_at').onSnapshot(snapshot => {
const todos = []
snapshot.forEach(doc => {
todos.push({ id: doc.id, title: doc.data().title })
})
commit('SET_TODO', todos)
})
}
请注意,这不会尝试处理可能的竞争条件。
我正在使用 Vue.js、Vuex 和 Firebase 构建一个待办事项列表应用程序。该应用程序似乎可以正常工作,因为 Store 文件成功地管理了输入的待办事项(进出 firestore)的检索和呈现。但是,我对在 Vuex 中设置参数仍然有疑问。突变中的 REMOVE_TODO 函数(参见 store.js)似乎需要两个参数,即使 "id" 是函数实际代码中引用的唯一参数。换句话说,如果我取出初始参数(在本例中为 "state"),那么控制台将 return 一个错误,内容为:"Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: a custom Object object"。我的问题是:如果 "id" 参数是函数中实际使用的唯一参数,为什么这个 REMOVE_TODO 函数需要有两个参数才能正常运行?为什么需要另一个参数?下面是我的代码。谢谢!
app.vue
<template>
<div id="app" class="container">
<input class="form-control" :value="newTodo" @change="getTodo" placeholder="I need to...">
<button class="btn btn-primary" @click="addTodo">Add New Post</button>
<ul class="list-group">
<li class="list-group-item" v-for="todo in this.$store.getters.getTodos" :key="todo.id">
{{todo.title}}
<div class="btn-group">
<button type="button" @click="remove(todo.id)" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-remove-circle"></span> Remove
</button>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
beforeCreate: function() {
this.$store.dispatch('setTodo')
},
methods: {
getTodo(event) {
this.$store.dispatch('getTodo', event.target.value)
},
addTodo() {
this.$store.dispatch('addTodo')
this.$store.dispatch('clearTodo')
},
remove(id){
this.$store.dispatch('removeTodo', id)
}
},
computed: {
newTodo() {
return this.$store.getters.newTodo
},
todos(){
return this.$store.getters.todos
}
}
}
</script>
<style>
body {
font-family: Helvetica, sans-serif;
}
li {
margin: 10px;
}
</style>
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import db from '../firebase'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
todos: [],
newTodo: '',
errors: ''
},
mutations: { //syncronous, committed
GET_TODO: (state, todo) => {
state.newTodo = todo
},
ADD_TODO: state => {
db.collection('items').add({
title: state.newTodo,
created_at: Date.now(),
}).then(function(){
console.log('Document successfully added')
})
.catch((error) => {
this.errors = error
})
},
REMOVE_TODO: (state, id) => {
if (id) {
db.collection("items").doc(id).delete().then(function() {
console.log('Document successfully deleted')
})
.catch((error) => {
this.errors = error
})
} else {
this.errors = 'Invalid ID'
}
},
CLEAR_TODO: state => {
state.newTodo = ''
},
SET_TODO: state => {
let todos = []
db.collection('items').orderBy('created_at').onSnapshot((snapshot) => {
todos = []
snapshot.forEach((doc) => {
todos.push({ id: doc.id, title: doc.data().title })
})
state.todos = todos
})
}
},
actions: { //asyncronous, dispatched
getTodo: (context, todo) => {
context.commit('GET_TODO', todo)
},
addTodo: context => {
context.commit('ADD_TODO')
},
removeTodo: (context, id) => {
context.commit('REMOVE_TODO', id)
},
clearTodo: context => {
context.commit('CLEAR_TODO')
},
setTodo: context => {
context.commit('SET_TODO')
}
},
getters: {
newTodo: state => state.newTodo,
getTodos: state => {
return state.todos
}
}
})
这与 Vuex 没有任何关系,只是 JavaScript 函数调用的工作方式。参数按位置而不是名称传递。
你有 REMOVE_TODO: (state, id) => {
但名称 state
和 id
只有在函数内部才真正有意义。在函数之外,从调用者的角度来看,这些名称并不重要。它可以很容易地成为 REMOVE_TODO: (a, b) => {
.
当 Vuex 调用 mutation 时,它将传递 state
对象作为第一个参数,payload
作为第二个参数。实际上它在调用:
mutations.REMOVE_TODO(state, payload)
我重申,参数的名称实际上并不重要,重要的是它们的位置。
这是 Vuex 内部的,不是你可以直接控制的。如果您自己调用该函数,则可以传递任何您想要的东西,但您没有(也不应该)。相反,您(非常正确地)调用 commit
。您将所需的 payload
传递给 commit
,然后 Vuex 将完成剩下的工作,调用相关的突变并将其传递给 state
和 payload
.
将函数定义为 REMOVE_TODO: (id) => {
不会更改调用方传递的参数。第一个参数仍然是 state
对象。您已将其命名为 id
但这没有任何区别,它仍然是 state
对象。
一般来说,尝试删除第一个参数的问题是所有其他参数有效地向下排列,因此所需参数名称的位置不再与传递的参数的位置匹配。从最后删除一个参数没有这样的问题,因为其他参数不会移动位置。所以如果你只需要 state
但不需要 payload
那么你可以只删除第二个参数没问题。 API 设计者总是尝试将可选参数放在末尾,这样它们可以被删除而不会导致任何问题。
对于突变,payload
很容易是不必要的,但 state
总是需要的。突变的全部意义在于改变 state
。如果你不改变 state
那么你不应该使用突变。
如您的代码中所述,actions
是异步的,而 mutations
需要是同步的。但这不是你所拥有的。目前,您的突变中有很多对 Firebase 的异步调用。所有这些都需要移入 actions
。请注意,Promises 始终是异步的,因此如果您发现自己在突变中调用 then
,那您就错了。唯一应该在 mutations
内的部分是当您修改 state
.
您还需要避免在 Vuex 存储中使用 this
。你应该发现你需要的一切都可以从传递给函数的参数中访问。目前你有 this.errors = error
和类似的突变。我假设应该是 state.errors = error
.
更新:
根据评论中的要求:
SET_TODO (state, todos) {
state.todos = todos
}
setTodo ({commit}) {
db.collection('items').orderBy('created_at').onSnapshot(snapshot => {
const todos = []
snapshot.forEach(doc => {
todos.push({ id: doc.id, title: doc.data().title })
})
commit('SET_TODO', todos)
})
}
请注意,这不会尝试处理可能的竞争条件。