我正在使用Vue.js,Vuex和Firebase构建待办事项列表应用程序。该应用程序似乎可以正常运行,因为“商店”文件成功管理了输入的待办事项(与Firestore之间的关联)的检索和渲染。但是,我仍然对在Vuex中设置参数有疑问。变异中的REMOVE_TODO函数(请参阅store.js)似乎需要两个参数,即使“ id”是该函数实际代码中唯一引用的参数。换句话说,如果我删除了初始参数(在这种情况下为“ state”),则控制台将返回一个错误消息:“ Function CollectionReference.doc()要求其第一个参数的类型为非空字符串,但是它是:一个自定义对象对象”。我的问题是:如果“ 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
}
}
})
答案 0 :(得分:1)
这与Vuex无关,这只是JavaScript函数调用的工作方式。参数是按位置而不是名称传递的。
您有REMOVE_TODO: (state, id) => {
,但名称state
和id
仅在函数内有意义。在函数之外,从调用者的角度来看,这些名称无关紧要。就像REMOVE_TODO: (a, b) => {
一样容易。
Vuex调用变异时,它将传递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
中。请注意,Promise始终是异步的,因此,如果您发现自己在突变内调用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)
})
}
请注意,这不会尝试应对可能的比赛条件。