学习 Vue.js:用 Vuex 重构 Todo 项目
在 上一篇 中,vue.js 与 Laravel 结合实现了 Todo 项目的数据持久化,下面用 Vuex 重构。
安装 Vuex
$ npm install vuex --save
修改 main.js
,引入 Vuex。
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import Vuex from 'vuex'
import App from './App'
import router from './router'
Vue.use(VueAxios, axios)
Vue.use(Vuex)
Vue.config.productionTip = false
const store = new Vuex.Store({
state: {
todos: [],
newTodo: { id: null, title: '', completed: false }
},
mutations: {
// ...
},
actions: {
// ...
}
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
Vuex 仅以一个全局对象(store
)的形式呈现,就可以管理整个项目里所有的组件状态。state
对象里的 todos
和 newTodo
可简单理解为全局变量,这两个变量之所以对项目中所有组件可见,是因为在 new Vue
的时候注册入项目里了。
new Vue({
...
store,
...
})
App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
mounted() {
this.$store.dispatch('getTodos')
}
}
</script>
在组件里使用 this.$store
取得注入的 store
对象。我们把获得 Todo 列表的工作 dispatch 给了 store
对象的 getTodos action。
// main.js
const store = new Vuex.Store({
state: {
todos: [],
newTodo: { id: null, title: '', completed: false }
},
mutations: {
get_todos (state, todos) {
state.todos = todos
}
},
actions: {
getTodos (store) {
Vue.axios.get('http://localhost:8000/api/todos').then((response) => {
store.commit('get_todos', response.data)
})
}
}
}
getTodos action 取得 Todo 列表,把结果 response.data
commit 给了 get_todos mutation; get_todos mutation 接收的第二个参数就是传递过去的 Todo 列表,然后赋值给了 state.todos
。接下来在 Todos.vue
可直接使用 todos
变量了。
Todos.vue
// Todos.vue
<script>
export default {
name: 'todos',
computed: {
todos () {
return this.$store.state.todos
},
newTodo () {
return this.$store.state.newTodo
}
}
}
</script>
通过 this.$store.state
就能取得 todos
和 newTodo
,然后就可以在组件 <template>
中直接使用了。
增删改查
与 Todo 项目增删改查的操作,与实现显示 Todo 列表的流程相似,这里直接贴出代码。共涉及三个文件 main.js
、Todos.vue
和 App.vue
。
main.js
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import Vuex from 'vuex'
import App from './App'
import router from './router'
Vue.use(VueAxios, axios)
Vue.use(Vuex)
Vue.config.productionTip = false
const store = new Vuex.Store({
state: {
todos: [],
newTodo: { id: null, title: '', completed: false }
},
mutations: {
get_todos (state, todos) {
state.todos = todos
},
add_todo (state, todo) {
state.todos.push(todo)
state.newTodo.title = ''
},
delete_todo (state, index) {
state.todos.splice(index, 1)
},
toggle_done (state, todo) {
todo.completed = !todo.completed
}
},
actions: {
getTodos (store) {
Vue.axios.get('http://localhost:8000/api/todos').then((response) => {
store.commit('get_todos', response.data)
})
},
addTodo (store) {
if (! store.state.newTodo.title.trim()) { return ; }
Vue.axios.post('http://localhost:8000/api/todos/create', { 'title': store.state.newTodo.title })
.then((response) => {
store.commit('add_todo', response.data)
});
},
deleteTodo (store, payload) {
Vue.axios.delete('http://localhost:8000/api/todo/' + payload.todo.id + '/delete')
.then((response) => {
store.commit('delete_todo', payload.index)
});
},
toggleDone (store, todo) {
Vue.axios.patch('http://localhost:8000/api/todo/' + todo.id + '/toggleComplete')
.then((response) => {
store.commit('toggle_done', todo)
});
}
}
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
mounted() {
this.$store.dispatch('getTodos')
}
}
</script>
Todos.vue
<template>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-default">
<div class="panel-heading text-center">
计划要做的事情,共 {{ todos.length }} 件
</div>
<div class="panel-body">
<div class="list-groups">
<a class="list-group-item" v-bind:class="{ 'completed': todo.completed }" v-for="(todo, index) in todos">
<router-link :to="{ name: 'Todo', params: { id: todo.id }}">{{ todo.title }}</router-link>
<button class="btn btn-xs btn-danger pull-right" v-on:click='destroy(todo, index)'title="删除">✘</button>
<button class="btn btn-xs pull-right" v-on:click='toggleDone(todo)' v-bind:class="[todo.completed ? 'btn-success': '']" v-bind:title="[todo.completed ? '点击,标记为未完成': '点击,标记为已完成']">✔</button>
</a>
</div>
</div>
<div class="panel-footer">
<form v-on:submit.prevent="add">
<div class="form-group">
<input type="text" class="form-control text-center" v-model="newTodo.title">
</div>
<button class="btn btn-default btn-block" type="submit">添加</button>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'todos',
computed: {
todos () {
return this.$store.state.todos
},
newTodo () {
return this.$store.state.newTodo
}
},
methods: {
add() {
this.$store.dispatch('addTodo')
},
destroy(todo, index) {
this.$store.dispatch('deleteTodo', { todo, index })
},
toggleDone(todo) {
this.$store.dispatch('toggleDone', todo)
}
}
}
</script>
本作品采用《CC 协议》,转载必须注明作者和本文链接
好眼熟 这不是教主的教程么... @jellyBool
@Rekkles 这就是教主的,我记下来,方便看的。
表示还没用过 Vuex , mark一下.