🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 一、概述 为了方便在组件中使用,可以把vuex中定义的那些state、mutation、action及getters在组件里面做映射。这样,就可以大大简化了对状态组件的使用了; >[danger] 简单的理解,就是通过映射,把vuex中定义的那些元素,映射到组件的定义中来,直接通过映射的方法名就可以使用了,而不用再通过详细的代码过程来调用; ## 二、state与mapState 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState辅助函数帮助我们生成计算属性; 举例来说: ```js const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } } } ``` 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给mapState传一个字符串数组。 ``` computed: mapState([ // 映射 this.count 为 store.state.count 'count' ]) ``` mapState函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给computed属性。 ``` computed: { localComputed () { /* ... */ }, // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ // ... }) } ``` ## 三、getter与mapGetters 有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数: ``` computed: { doneTodosCount () { return this.$store.state.todos.filter(todo => todo.done).length } } ``` Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 Getter 接受 state 作为其第一个参数: ``` const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) } } }) ``` Getter 会暴露为`store.getters`对象: ``` store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] ``` Getter 也可以接受其他 getter 作为第二个参数: ``` getters: { // ... doneTodosCount: (state, getters) => { return getters.doneTodos.length } } store.getters.doneTodosCount // -> 1 ``` ### 组件中使用: 我们可以很容易地在任何组件中使用它: ``` computed: { doneTodosCount () { return this.$store.getters.doneTodosCount } } ``` mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性; ``` import { mapGetters } from 'vuex' export default { // ... computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) } } ``` 如果你想将一个 getter 属性另取一个名字,使用对象形式: ``` mapGetters({ // 映射 `this.doneCount` 为 `store.getters.doneTodosCount` doneCount: 'doneTodosCount' }) ``` ## 四、mutation和mapMutations 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的**事件类型 (type)**和 一个**回调函数 (handler)**。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数: ``` const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 变更状态 state.count++ } } }) ``` ``` store.commit('increment') ``` 当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变; ``` mutations: { increment (state, payload) { state.count += payload.amount } } ``` 使用常量替代 Mutation 事件类型, ``` // mutation-types.js export const SOME_MUTATION = 'SOME_MUTATION' // store.js import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名 [SOME_MUTATION] (state) { // mutate state } } }) ``` ### 组件中使用: 你可以在组件中使用 this.$store.commit('xxx') 提交 mutation。 或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。 ``` import { mapMutations } from 'vuex' export default { // ... methods: { ...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')` }) } } ``` ## 五、action与mapActions Action 类似于 mutation,不同在于: Action 提交的是 mutation,而不是直接变更状态; Action 可以包含任意异步操作; Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用`context.commit`提交一个 mutation,或者通过`context.state`和`context.getters`来获取 state 和 getters。 ``` actions: { increment ({ commit }) { commit('increment') } } ``` 分发: Action 通过`store.dispatch`方法触发: ``` store.dispatch('increment') ``` 乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得**mutation 必须同步执行**这个限制么?Action 就不受约束!我们可以在 action 内部执行**异步**操作: ``` actions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } } ``` Actions 支持同样的载荷方式和对象方式进行分发: ``` // 以载荷形式分发 store.dispatch('incrementAsync', { amount: 10 }) // 以对象形式分发 store.dispatch({ type: 'incrementAsync', amount: 10 }) ``` ### 组件中使用: 你在组件中使用`this.$store.dispatch('xxx')`分发 action,或者使用`mapActions`辅助函数将组件的 methods 映射为`store.dispatch`调用(需要先在根节点注入`store`): ``` 用法: // 引入 mapActions import { mapActions } from 'vuex'; // 进行解构赋值和拓展运算 export default { // ... methods: { //下述中的 ... 是拓展运算符 // 使用 [] 是解构赋值 ...mapActions([ 'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')` // `mapActions` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)` ]), ...mapActions({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')` }) } } ``` 注意: 1. mapActions 必须放在 methods中,因为 action 或者  mutation 都是函数. 2. mapAction 里面都是store 里面的集合,所以使用ES6中解构赋值的方法进行获取我们所需的方法。 3. mapAction 前面的 ( ... ) 是ES6中 拓展运算符,对我们所需的方法从数组中拓展出来。 4. ES6对象中同名属性可以简写。 5. 也可以自己命名不同函数名来映射 action方法 ## 六、createNamespacedHelpers `createNamespacedHelpers(namespace: string): Object` 创建基于命名空间的组件绑定辅助函数。其返回一个包含`mapState`、`mapGetters`、`mapActions`和`mapMutations`的对象。它们都已经绑定在了给定的命名空间上。 ## 七、实例 ```vue <template> //直接使用state的代码 {{this.$store.state.views}} //mapState方式 {{viewsCount}} </template> ``` ```js <script> import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' export default { data () { return { checked: true }}, created () { // this.$store.dispatch('addViews') // 直接通过store的方法 触发action, 改变 views 的值 this.blogAdd() // 通过mapActions 触发mutation 从而commit ,改变state的值 }, computed: { ...mapState({ viewsCount: 'views' }), ...mapGetters({ todosALise: 'getToDo' // getToDo 不是字符串,对应的是getter里面的一个方法名字 然后将这个方法名字重新取一个别名 todosALise })}, methods: { ...mapMutations({ totalAlise: 'clickTotal' // clickTotal 是mutation 里的方法,totalAlise是重新定义的一个别名方法,本组件直接调用这个方法 }), ...mapActions({ blogAdd: 'blogAdd' // 第一个blogAdd是定义的一个函数别名称,挂载在到this(vue)实例上,后面一个blogAdd 才是actions里面函数方法名称 })} } </script> ```