学习用Vuex构建一个Notes App
Vuex是一套解决vue中组件通信的较完备解决方案。该文通过一个简单的例子来学习如何使用Vuex这个状态管理模块。
项目地址:vuex-notes-app
引自:notes-app-vuejs-vuex
Vuex基础概念
Vuex是一种全局单例模式管理,所有组件对其共用;同时,将状态变量的定义(State)
、调用(Getter)
以及变更(Mutation/Action)
隔离开,是代码书写更加结构化。按照规范去修改状态,这样借助官方提供的devtools,我们便可对mutation进行日志记录,在debug时大有裨益。这边不对每块基础内容进行详情解释,详见官方文档Vuex。下面开始,动手通过Vuex构建一个Notes App吧。
项目构建
我们需要实现的Notes App如下:
![](https://img.haomeiwen.com/i2279614/9b083b603fe1a919.png)
需要实现的Note分为三部分:
- Toolbar,工具边栏,有add、collect以及delete
- NoteList,Notes列表,包含All Notes以及Favorites两个列表,Tab用以切换
- Editor,Note编辑区域
因此,项目也为分割三个组件;同时,我们这次的主题是Vuex,所以我们会将所有的数据都使用Vuex来维护。我们使用vue-cli的simple-webpack搭建一个项目,项目结构如下:
![](https://img.haomeiwen.com/i2279614/c4bf4e2c64b3f04d.png)
使用Vuex构建Store
在src/store
目录下,我么创建一个store.js
。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定义所需的状态变量
},
getters: {
// 获取状态数据
},
mutations: {
// 修改状态数据(同步操作)
},
actions: {
// 修改状态数据(异步操作)
}
})
为了让所有组件能够更方便地使用store,不需要在组件内频繁手动导入,我们需要在main.js
中将其在实例中进行注册
import Vue from 'vue'
import App from './App.vue'
import store from './store/store.js'
new Vue({
el: '#app',
store,
render: h => h(App)
})
接下来,我们来定义需要共享的state
首先,定义notes: []
数组用来存放创建的notes
-
Toolbar
中的add/delete
需要对其进行增删 -
NoteList
需要对其进行列表渲染;
然后,需要定义activeNote: {}
来存放当前正在操作的note
-
Toolbar
中delete
动作会在notes: []
中找到activeNote: {}
进行删除 -
Toolbar
中collect
动作会其置位favourite
-
NoteList
中列表点击会变更activeNote
-
Editor
中会修改activeNote
中的内容
根据以上内容,我们来丰富我们的store
中的内容
state: {
notes: [],
activeNote: {}
},
mutations: {
ADD_NOTE (state){
const newNote = {
text: "New note",
favorite: false
}
state.notes.push(newNote)
state.activeNote = newNote
},
DELETE_NOTE (state){
let index = state.notes.indexOf(state.activeNote)
if(index > -1){
state.notes.splice(index,1)
}
state.activeNote = state.notes[0]
},
TOGGLE_FAVORITE (state){
state.activeNote.favorite = !state.activeNote.favorite
},
SET_ACTIVE_NOTE (state, note) {
state.activeNote = note
},
EDIT_NOTE (state, text) {
state.activeNote.text = text
}
}
至此,我们已经定义完了store
中的state
以及mutation
。
下面来看看需要定义哪些getter
来获取state
-
NoteList
中需要获取notes
来渲染全部notes列表以及favoriteNotes
来渲染favorite notes列表 -
Editor
中需要渲染activeNote
中的内容text
所以, store
中getter
内容如下:
getters: {
notes: state => state.notes,
activeNote: state => state.activeNote,
favoriteNotes: state => {
return state.notes.filter(note => note.favorite)
}
}
至此,我们整个store
就完工了。接下来,我们会在组件中去调用store
中各项内容。
创建组件
由于上面,我们已经根据数据以及操作捋好了思路,创建组件的工作就顺其自然了:
- 切页面
- 获取
store
中数据进行渲染 - 根据页面操作修改
store
中的数据
根组件(App.vue)
<template lang="html">
<div id="app">
<toolbar></toolbar>
<notes-list></notes-list>
<editor></editor>
</div>
</template>
<script>
import Toolbar from './components/Toolbar.vue'
import NotesList from './components/NotesList.vue'
import Editor from './components/Editor.vue'
export default {
name: 'app',
components: {
Toolbar,
NotesList,
Editor
}
}
</script>
子组件--Toolbar
各组件中样式,我们都是沿用了原项目的;样式文件在src/styles.css
。在Toolbar
中我们需要获取当前正在编辑的activeNote
来判断其是否被favorite
。由于,我们根组件中注册了store
,所以我们不需要再重复引入。当在组件中需要调用多个mutation
时,可以使用官方提供的mapMutations
来简化书写。
<template>
<div id="toolbar">
<i @click="addNote" class="glyphicon glyphicon-plus"></i>
<i @click="toggleFavorite"
class="glyphicon glyphicon-star"
:class="{starred: activeNote.favorite}"></i>
<i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
activeNote() {
return this.$store.getters.activeNote
}
},
methods: {
...mapMutations({
addNote: 'ADD_NOTE',
deleteNote: 'DELETE_NOTE',
toggleFavorite: 'TOGGLE_FAVORITE'
})
}
}
</script>
子组件--NotesList
同时,当在组件中需要获取多个state时,可以通过mapGetters
来简化书写
<template>
<div id="notes-list">
<div id="list-header">
<h2>Notes | coligo</h2>
<div class="btn-group btn-group-justified" role="group">
<!-- All Notes button -->
<div class="btn-group" role="group">
<button type="button" class="btn btn-default"
@click="show = 'all'"
:class="{active: show === 'all'}">
All Notes
</button>
</div>
<!-- Favorites Button -->
<div class="btn-group" role="group">
<button type="button" class="btn btn-default"
@click="show = 'favorites'"
:class="{active: show === 'favorites'}">
Favorites
</button>
</div>
</div>
</div>
<!-- render notes in a list -->
<div class="container">
<div class="list-group">
<a v-for="note in filteredNotes"
class="list-group-item" href="#"
:class="{active: activeNote == note}"
@click="updateActiveNote(note)">
<h4 class="list-group-item-heading">
{{note.text.trim().substring(0, 30)}}
</h4>
</a>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data () {
return {
show: 'all'
}
},
computed: {
...mapGetters([
'notes',
'activeNote',
'favoriteNotes'
]),
filteredNotes () {
return this.show === 'all' ? this.notes : this.favoriteNotes
}
},
methods: {
updateActiveNote(note) {
this.$store.commit('SET_ACTIVE_NOTE', note)
}
}
}
</script>
子组件--Editor
<template>
<div id="note-editor">
<textarea
:value="activeNote.text"
@input="editNote"
class="form-control">
</textarea>
</div>
</template>
<script>
export default {
computed: {
activeNote() {
return this.$store.getters.activeNot
}
},
methods: {
editNote(e){
this.$store.commit('EDIT_NOTE', e.target.value)
}
}
}
</script>
至此,一个基于Vuex进行数据构建的Note App就完成了。具体代码戳这里vuex-notes-app。