Vuex 是什么?
Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.
Here is a vuex store definition:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
},
actions: {
},
mutations: {
},
getters: {
},
modules: {
}
})
export default store
There are 5 objects that make up a store:
state:
This is where you define the data structure of your application. You can also set default or initial state here.actions:
Actions are where you define the calls that will commit changes to your store. A common example of this would be a call to an api to retrieve data, once it completes you call store.commit() to invoke a mutation to actually update the data in the store. Actions are called in your components via dispatch call.mutations:
The mutations calls are the only place that the store can be updated.getters:
Getters are a way to grab computed data from the store.modules:
The modules object provides a way to split your store in multiple stores, but allow them to all remain part of the store tree. As your application grows this is a good way to organize your codebase.
更多详情参看 中文文档
几点说明:
- 数据是单向流动的
- 组件可以调用 actions
- actions 用来调度/提交 mutations
- 只有 mutations 可以修改 state,组件不能也不应该直接修改 state
- 无论 state 何时更新,组件都将反映这些变化
开始项目
初始化项目
vue init webpack vuex-notes-app
cd vuex-notes-app
npm install
npm install vuex --save
npm run dev
创建 Vuex Store
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
notes: [],
activeNote: {}
}
const mutations = {
ADD_NOTE(state) {
const newNote = {
text: 'New note',
favorite: false
}
state.notes.push(newNote)
state.activeNote = newNote
},
EDIT_NOTE(state, text) {
state.activeNote.text = text
},
DELETE_NOTE(state) {
if (state.notes.length > 0) {
state.notes.splice(state.notes.indexOf(state.activeNote), 1)
state.activeNote = state.notes[0]
}
},
TOGGLE_FAVORITE(state) {
if (state.activeNote) {
state.activeNote.favorite = !state.activeNote.favorite
}
},
SET_ACTIVE_NOTE(state, note) {
state.activeNote = note
}
}
const getters = {
activeNoteText: state => {
if (state.activeNote) {
return state.activeNote.text
}
},
activeNoteFavorite: state => {
if (state.activeNote) {
return state.activeNote.favorite
}
},
}
const actions = {
addNote({ commit }) {
commit('ADD_NOTE')
},
editNote({ commit }, e) {
commit('EDIT_NOTE', e.target.value)
},
deleteNote({ commit }) {
commit('DELETE_NOTE')
},
updateActiveNote({ commit }, note) {
commit('SET_ACTIVE_NOTE', note)
},
toggleFavorite({ commit }) {
commit('TOGGLE_FAVORITE')
}
}
export default new Vuex.Store({
state,
mutations,
getters,
actions
})
导入
// main.js
// import the store and add it to the root vue object
import Vue from 'vue'
import App from './App'
import store from './vuex/store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
template: '<App/>',
components: {
App
},
store,
})
Toolbar.vue
<template>
<div id="toolbar">
<i @click="addNote" class="glyphicon glyphicon-plus"></i>
<i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNoteFavorite}"></i>
<i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
methods: {
...mapActions(['addNote', 'deleteNote', 'toggleFavorite'])
},
computed: {
...mapGetters(['activeNoteFavorite'])
}
}
</script>
NotesList.vue
<template>
<div id="notes-list">
<div id="list-header">
<h2>Notes | Awesome</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" :key="note" 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 { mapActions, mapState } from 'vuex'
export default {
data() {
return {
show: 'all'
}
},
methods: {
...mapActions(['updateActiveNote'])
},
computed: {
...mapState(['notes', 'activeNote']),
filteredNotes() {
if (this.notes) {
if (this.show === 'all') {
return this.notes
} else if (this.show === 'favorites') {
return this.notes.filter(note => note.favorite)
}
}
}
}
}
</script>
Editor.vue
<template>
<div id="note-editor">
<textarea :value="activeNoteText" @input="editNote" class="form-control">
</textarea>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['activeNoteText'])
},
methods: {
...mapActions(['editNote'])
}
}
</script>
App.vue
<template>
<div id="app">
<Toolbar></Toolbar>
<NotesList></NotesList>
<Editor></Editor>
</div>
</template>
<script>
import Toolbar from './components/Toolbar'
import NotesList from './components/NotesList'
import Editor from './components/Editor'
export default {
name: 'app',
components: {
Toolbar,
NotesList,
Editor
}
}
</script>
styles.css
@import url(https://fonts.googleapis.com/css?family=Raleway:400,300);
html, #app {
height: 100%;
}
body {
margin: 0;
padding: 0;
border: 0;
height: 100%;
max-height: 100%;
position: relative;
}
#toolbar {
float: left;
width: 80px;
height: 100%;
background-color: #30414D;
color: #767676;
padding: 35px 25px 25px 25px;
}
#notes-list {
float: left;
width: 300px;
height: 100%;
background-color: #F5F5F5;
font-family: 'Raleway', sans-serif;
font-weight: 400;
}
#list-header {
padding: 5px 25px 25px 25px;
}
#list-header h2 {
font-weight: 300;
text-transform: uppercase;
text-align: center;
font-size: 22px;
padding-bottom: 8px;
}
#notes-list .container {
height: calc(100% - 137px);
max-height: calc(100% - 137px);
overflow: auto;
width: 100%;
padding: 0;
}
#notes-list .container .list-group-item {
border: 0;
border-radius: 0;
}
.list-group-item-heading {
font-weight: 300;
font-size: 15px;
}
#note-editor {
height: 100%;
margin-left: 380px;
}
#note-editor textarea {
height: 100%;
border: 0;
border-radius: 0;
}
#toolbar i {
font-size: 30px;
margin-bottom: 35px;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.5s ease;
}
#toolbar i:hover {
opacity: 1;
}
.starred {
color: #F7AE4F;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Notes</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/styles.css">
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>