对 vue 应用中多个组件的共享状态进行集中式的管理(读/写)
GitHub: https://github.com/vuejs/vuex
在线文档: https://github.com/vuejs/vuex
单向数据流理念
- state: 驱动应用的数据源(data数据)
- view: 以声明方式将state映射到视图(初始化数据和更新显示数据)
- actions: 响应在 view 上的用户输入导致的状态变化(多个事件函数改变数据)
核心概念
- state vuex 管理的状态对象(初始化data)
- mutations 直接更新state的方法(回调函数)的对象
- actions 事件回调函数的对象, 发送ajax请求跟后台交互
- getter 计算属性
- modules 外部模板(xxx.vue)
- store 在main.js配置后会生成一个$store属性, 它就是store对象,
其中有属性state, getters
方法dispatch(actionName, data): 分发调用action
使用
下载: npm install --save vuex
创建: (src/store/store.js)新建store.js(存放核心管理对象模块), 向外暴露store对象
- store.js
/**
* vuex 的 store 对象模块
*/
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
/*
state 对象
类似于 data
*/
const state = {
count: 0 // 初始化状态数据
}
/*
mutations 对象
包含多个方法: 能直接更新 state
mutation 只能包含更新 state 的同步代码, 也不会有逻辑
mutation 由 action 触发调用: commit('mutationName')
*/
const mutations = {
INCREMENT(state) {
state.count++
},
DECREMENT(state) { // ctrl + shift + x
state.count--
}
}
/*
actions 对象
包含多个方法: 触发 mutation 调用, 间接更新 state
action 中可以有逻辑代码和异步代码
action 由组件来触发调用: this.$store.dispatch('actionName')
*/
const actions = {increment({commit}) {
commit('INCREMENT')
},
decrement({commit}) {
commit('DECREMENT')
},
incrementIfOdd({commit,state}) {
if (state.count % 2 === 1) {
commit('INCREMENT')
}
},
incrementAsync({commit}) {
setTimeout(() => {
commit('INCREMENT')
}, 1000)
}
}
/*
getters 对象
包含多个 get 计算计算属性方法
*/
const getters = {
oddOrEven(state) {
return state.count % 2 === 0 ? '偶数' : '奇数'
},
count(state) {
return state.count
}
}
// 向外暴露 store 实例对象
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
- 在main.js配置
import store from './store'
new Vue({
el: '#app',
router,
store, //这里配置后所有的组件对象多了个属性: $store
render: h => h(App)
})
- 组件中使用
<template>
<div>
<p>clicked: {{$store.state.count}} times, count is {{oddOrEven}}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">increment if odd</button>
<button @click="incrementAsync">increment async</button>
</div>
</template>
<script>
export default {
computed: {
oddOrEven() {
return this.$store.getters.oddOrEven;
}
},
methods: {
increment() {
this.$store.dispatch("increment");
},
decrement() {
this.$store.dispatch("decrement");
},
incrementIfOdd() {
this.$store.dispatch("incrementIfOdd");
},
incrementAsync() {
this.$store.dispatch("incrementAsync");
}
}
};
</script>
<style>
</style>
上面代码可以优化
<template>
<div>
<p>clicked: {{count}} times, count is {{oddOrEven2}}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">increment if odd</button>
<button @click="incrementAsync">increment async</button>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
computed: mapGetters({
// 名称和getters中的名称不一致(oddOrEven2: "oddOrEven")
oddOrEven2: "oddOrEven",
count: "count"
}),
methods: mapActions([
// 名称和actions中的名称一致
"increment",
"decrement",
"incrementIfOdd",
"incrementAsync"
])
};
</script>
<style>
</style>
分模块使用
创建store文件夹(src/store), 在文件夹下创建以下文件
- index.js store的入口文件(相当于store.js), vuex核心管理对象, 向外暴露store对象
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
- state.js 初始化变量(相当于data)
export default {
todos: []
}
- actions.js 接受组件事件, 触发mutation调用, 本身不做逻辑处理, 只是分发事件
this.$.store.dispatch('事件名',参数)
import {
ADD_TODO,
DELETE_TODO,
SELECT_ALL_TODOS,
CLEAR_ALL_COMPLETED
} from './mutations-types'
export default {
// 组件的addTodo方法,todo为参数, commit把方法提交到mutation
addTodo({commit}, todo) {
// 这里传参必须是对象包裹
commit(ADD_TODO, {todo})
},
deleteTodo({commit}, index) {
commit(DELETE_TODO, {index})
},
selectAllTodos({commit}, check){
commit(SELECT_ALL_TODOS, {check})
},
clearAllCompleted({commit}){
commit(CLEAR_ALL_COMPLETED)
}
}
- mutations-types.js 定义mutations的名称常量模块, 这样做的好处解决commit中名称命名问题, 同时防止了名称重复命名
export const ADD_TODO = 'add_todo'
export const DELETE_TODO = 'delete_todo'
export const SELECT_ALL_TODOS = 'select_all_todos'
export const CLEAR_ALL_COMPLETED = 'clear_all_completed'
- mutations.js 接受actions.js分发过来的事件, 编写逻辑处理, 改变state 中的变量
import {
ADD_TODO,
DELETE_TODO,
SELECT_ALL_TODOS
} from './mutations-types'
export default {
[ADD_TODO](state, {todo}) {
state.todos.unshift(todo)
},
[DELETE_TODO](state, {index}) {
state.todos.splice(index, 1)
},
[SELECT_ALL_TODOS](state, {check}) {
state.todos.forEach(todo => todo.complete = check)
},
[CLEAR_ALL_COMPLETED](state) {
state.todos = state.todos.filter(todo => !todo.complete)
},
}
- getters.js 计算属性, 对state数据处理
export default{
// 总数量
totalCount (state){
return state.todos.length
},
// 完成数量
completeCount (state){
return state.todos.reduce(
(preTotal, todo) => preTotal + (todo.complete ? 1 : 0),0);
},
// 判断是否全部选中
isAllCheck (state, getters){
return getters.totalCount===getters.completeCount && state.todos.length>0
}
}
组件中读取数据
- state 引入mapState, 在computed中读取
- getters 引入mapGetters, 在computed中读取
import {mapState} from 'vuex'
export default {
computed: {
...mapState(['todos'])
}
}
组件中修改数据
- this.$store.dispatch('名称',参数)
- mapActions定义事件
<button class="btn btn-danger" @click="deleteItem">删除</button>
methods: {
deleteItem() {
this.$store.dispatch('deleteTodo', index)
}
}
<button class="btn btn-danger" @click="clearAllCompleted">清除已完成任务</button>
import {mapGetters,mapActions} from 'vuex'
methods: {
...mapActions(['clearAllCompleted'])
}