一、介绍
1.1 Vuex 是什么 ?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
1.2 什么是“状态管理模式”?
这个状态自管理应用包含以下几个部分:
状态,驱动应用的数据源;
视图,以声明方式将状态映射到视图;
操作,响应在视图上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的简单示意:
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
-
多个视图依赖于同一状态。
-
来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
Vuex就是把组件的共享状态抽取出来,以一个全局单例模式管理 。
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
1.3 安装
npm install vuex@next --save
1.4 简单的Vuex使用
1.4.1 store.js
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0},mutations: {},actions: {},
})
1.4.2 main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from "./store";Vue.config.productionTip = false/* eslint-disable no-new */
new Vue({el: '#app',components: { App },template: '<App/>',store
})
1.4.3 App.vue
<template><div><my-addition></my-addition><p>----------------------------------</p><my-subtraction></my-subtraction></div>
</template><script>
import Addition from "./components/Addition";
import Subtraction from "./components/Subtraction";export default {data() {return {};},components: {'my-addition': Addition,'my-subtraction': Subtraction}
}
</script><style></style>
1.4.4 Addition.vue
<template><div><!-- 组件访问State中数据的第一种方式 --><h3>当前最新的count值为:{{$store.state.count}}</h3><button>+1</button></div>
</template><script>
export default {name: "Addition",data() {return {};}
}
</script><style scoped></style>
1.4.5 Subtraction.vue
<template><div><h3>当前最新的count值为:{{$store.state.count}}</h3><button>-1</button></div>
</template><script>
export default {name: "Subtraction",data() {return {};}
}
</script><style scoped></style>
二、State
2.1 单一状态树
-
Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。
-
它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。
2.2 方式一:通过$store获取State中数据
$store.state.count
2.3 方式二:通过mapState获取State中数据(推荐)
// 组件访问State中数据的第二种方式--引入
import {mapState} from 'vuex'// 将全局数据,映射为当前组件的计算属性
...mapState(['count'])// 模版中使用
<template><div><h3>当前最新的count值为:{{count}}</h3><button>-1</button></div>
</template>
三、Mutation
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
3.1 方式一:使用commit触发mutation
使用commit触发mutation
// store.js
export default new Vuex.Store({state: {count: 0},mutations: {add(state) {state.count++;}},actions: {},
})// Adition.vue
<template><div><!-- 组件访问State中数据的第一种方式 --><h3>当前最新的count值为:{{$store.state.count}}</h3><button @click="addHandler">+1</button></div>
</template><script>
export default {name: "Addition",data() {return {};},methods: {addHandler() {// 这种方式是不合法的!!!// this.$store.state.count++;// 触发mutation的第一种方式this.$store.commit('add')}}
}
</script><style scoped></style>
带参数的mutation
// store.js
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0},mutations: {add(state) {state.count++;},// 第二位就是,传入的参数。如果是多个参数可以传入对象addN(state, step) {state.count += step;},sub(state) {state.count--;},},actions: {},
})
// Addition.vue
<template><div><!-- 组件访问State中数据的第一种方式 --><h3>当前最新的count值为:{{$store.state.count}}</h3><button @click="addHandler">+1</button><button @click="addNHandler">+N</button></div>
</template><script>
export default {name: "Addition",data() {return {};},methods: {addHandler() {// 这种方式是不合法的!!!// this.$store.state.count++;// 触发mutation的第一种方式this.$store.commit('add')},addNHandler() {this.$store.commit('addN', 3)}}
}
</script><style scoped></style>
3.2 方式二:使用mapMutation触发mutation(推荐)
store.js
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0},mutations: {add(state) {state.count++;},addN(state, step) {state.count += step;},sub(state) {state.count--;},subN(state, step) {state.count -= step;},},actions: {},
})
Subtraction.vue
<template><div><h3>当前最新的count值为:{{count}}</h3><button @click="subHandler">-1</button><button @click="subNHandler">-N</button></div>
</template><script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations} from 'vuex'export default {name: "Subtraction",data() {return {};},computed: {// 将全局数据,映射为当前组件的计算属性...mapState(['count'])},methods: {...mapMutations(['sub', 'subN']),// 将全局mutations函数映射为组件中的函数subHandler() {this.sub();},subNHandler() {this.subN(3)}}
}
</script><style scoped></style>
四、Action
Action的作用是执行异步函数,因为mutation中无法执行异步函数。在mutation中使用异步函数会导致state的状态无法被及时追踪导致代码异常!!!
4.1 方式一:使用dispatch调用Action中的异步函数
4.1.1 store.js
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0},mutations: {add(state) {// setTimeout(() => {// // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪// state.count++;// }, 1000)state.count++;},addN(state, step) {state.count += step;},sub(state) {state.count--;},subN(state, step) {state.count -= step;},},actions: {// context: 可以理解为当前的Vuex.Store实例对象addAsync(context) {setTimeout(() => {// 在 actions 中,不能直接修改 state 中的数据// 必须通过 context.commit() 触发某个 mutations 才行context.commit('add')}, 1000)},addNAsync(context, step) {setTimeout(() => {// 在 actions 中,不能直接修改 state 中的数据// 必须通过 context.commit() 触发某个 mutations 才行context.commit('addN', step)}, 1000)}},
})
4.1.2 Addition.vue
<template><div><!-- 组件访问State中数据的第一种方式 --><h3>当前最新的count值为:{{$store.state.count}}</h3><button @click="addHandler">+1</button><button @click="addNHandler">+N</button><button @click="addHandlerAsync">+1 Async</button><button @click="addNHandlerAsync">+N Async</button></div>
</template><script>
export default {name: "Addition",data() {return {};},methods: {addHandler() {// 这种方式是不合法的!!!// this.$store.state.count++;// 触发mutation的第一种方式this.$store.commit('add')},addNHandler() {this.$store.commit('addN', 3)},addHandlerAsync() {// 这里的 dispatch 专门用来触发action函数this.$store.dispatch('addAsync')},addNHandlerAsync() {// 这里的 dispatch 专门用来触发action函数this.$store.dispatch('addNAsync', 3)}}
}
</script><style scoped></style>
4.2 方式二:使用mapAction调用Action中的异步函数
4.2.1 store.js
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0},mutations: {add(state) {// setTimeout(() => {// // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪// state.count++;// }, 1000)state.count++;},addN(state, step) {state.count += step;},sub(state) {state.count--;},subN(state, step) {state.count -= step;},},actions: {// context: 可以理解为当前的Vuex.Store实例对象addAsync(context) {setTimeout(() => {// 在 actions 中,不能直接修改 state 中的数据// 必须通过 context.commit() 触发某个 mutations 才行context.commit('add')}, 1000)},addNAsync(context, step) {setTimeout(() => {// 在 actions 中,不能直接修改 state 中的数据// 必须通过 context.commit() 触发某个 mutations 才行context.commit('addN', step)}, 1000)},subAsync(context) {setTimeout(() => {context.commit('sub')}, 1000)},subNAsync(context, step) {setTimeout(() => {context.commit('subN', step)}, 1000)}},
})
4.2.2 Subtraction.vue
<template><div><h3>当前最新的count值为:{{count}}</h3><button @click="subHandler">-1</button><button @click="subNHandler">-N</button><button @click="subHandlerAsync">-1 Async</button><button @click="subNHandlerAsync">-N Async</button></div>
</template><script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations, mapActions} from 'vuex'export default {name: "Subtraction",data() {return {};},computed: {// 将全局数据,映射为当前组件的计算属性...mapState(['count'])},methods: {...mapMutations(['sub', 'subN']),...mapActions(['subAsync', 'subNAsync']),// 将全局mutations函数映射为组件中的函数subHandler() {this.sub();},subNHandler() {this.subN(3)},subHandlerAsync() {this.subAsync();},subNHandlerAsync() {this.subNAsync(3)}}
}
</script><style scoped></style>
五、Getters
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
5.1 创建Getter方法
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0},mutations: {add(state) {// setTimeout(() => {// // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪// state.count++;// }, 1000)state.count++;},addN(state, step) {state.count += step;},sub(state) {state.count--;},subN(state, step) {state.count -= step;},},actions: {// context: 可以理解为当前的Vuex.Store实例对象addAsync(context) {setTimeout(() => {// 在 actions 中,不能直接修改 state 中的数据// 必须通过 context.commit() 触发某个 mutations 才行context.commit('add')}, 1000)},addNAsync(context, step) {setTimeout(() => {// 在 actions 中,不能直接修改 state 中的数据// 必须通过 context.commit() 触发某个 mutations 才行context.commit('addN', step)}, 1000)},subAsync(context) {setTimeout(() => {context.commit('sub')}, 1000)},subNAsync(context, step) {setTimeout(() => {context.commit('subN', step)}, 1000)}},getters: {showNum(state) {return '当前最新的数量是【' + state.count + '】';}}
})
5.2 方法一:$store.getter调用
<template><div><!-- 组件访问State中数据的第一种方式 --><h3>{{$store.getters.showNum}}</h3><button @click="addHandler">+1</button><button @click="addNHandler">+N</button><button @click="addHandlerAsync">+1 Async</button><button @click="addNHandlerAsync">+N Async</button></div>
</template><script>
export default {name: "Addition",data() {return {};},methods: {addHandler() {// 这种方式是不合法的!!!// this.$store.state.count++;// 触发mutation的第一种方式this.$store.commit('add')},addNHandler() {this.$store.commit('addN', 3)},addHandlerAsync() {// 这里的 dispatch 专门用来触发action函数this.$store.dispatch('addAsync')},addNHandlerAsync() {// 这里的 dispatch 专门用来触发action函数this.$store.dispatch('addNAsync', 3)}}
}
</script><style scoped></style>
5.3 方法二:mapGetters方式调用
<template><div><h3>{{showNum}}</h3><button @click="subHandler">-1</button><button @click="subNHandler">-N</button><button @click="subHandlerAsync">-1 Async</button><button @click="subNHandlerAsync">-N Async</button></div>
</template><script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations, mapActions, mapGetters} from 'vuex'export default {name: "Subtraction",data() {return {};},computed: {// 将全局数据,映射为当前组件的计算属性...mapState(['count']),// 将全局的getters,映射为当前组件的计算属性...mapGetters(['showNum'])},methods: {...mapMutations(['sub', 'subN']),...mapActions(['subAsync', 'subNAsync']),// 将全局mutations函数映射为组件中的函数subHandler() {this.sub();},subNHandler() {this.subN(3)},subHandlerAsync() {this.subAsync();},subNHandlerAsync() {this.subNAsync(3)}}
}
</script><style scoped></style>
六、其他
6.1 modules
Module | Vuex
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {state: () => ({ ... }),mutations: { ... },actions: { ... },getters: { ... }
}const moduleB = {state: () => ({ ... }),mutations: { ... },actions: { ... }
}const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}
})store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态