Vuex系列文章

  《Vuex实践(上)》

  《Vuex实践(中)-多module中的state、mutations、actions和getters》

  《Vuex实践(下)-mapState和mapGetters》

一.前言

  本文章是vuex系列的最后一篇,主要总结的是如何使用mapState和mapGetters访问vuex中的state和getters。

二.多个模块中mapState和mapGetters的使用

  上一篇文章《Vuex实践(中)》里面我们总结的就是多模块的内容,所以关于store.js、moduleA.js和moduleB.js的代码保持不变。

  在此为了方便观看,我将这三个文件的代码在贴在这里

E:\MyStudy\test\VueDemo\src\vuex\store.js


1 import Vue from 'vue'
 2 import Vuex from 'vuex'
 3 import moduleA from './moduleA'
 4 import moduleB from './moduleB'
 5 
 6 Vue.use(Vuex)
 7 
 8 export default new Vuex.Store({
 9     state: {
10         counter: 1000
11     },
12     mutations: {
13         //递增
14         increase(state) {
15             console.log("store-increase")
16             state.counter++
17         },
18         //递减
19         decrement(state) {
20             state.counter--
21         }
22     },
23     actions: {
24         increaseAction(context) {
25             setTimeout(function(){
26                 //action通过提交mutation改变共享数据状态
27                 context.commit('increase');
28             },3000)
29         },
30         decrementAction(context){
31             setTimeout(function(){
32                 //action通过提交mutation改变共享数据状态
33                 context.commit('decrement');
34             },3000)
35         }
36     },
37     getters: {
38         doubleCounter(state) {
39             return state.counter*state.counter
40         }
41     },
42     modules: {
43         a: moduleA,
44         b: moduleB
45     }
46 })


E:\MyStudy\test\VueDemo\src\vuex\moduleA.js


const moduleA = {
    namespaced: true,
    state:{
        counter: 100
    },
    mutations: {
        //递增
        increase(state) {
            console.log("moduleA-increase")
            state.counter++
        },
        //递减
        decrement(state) {
            state.counter--
        }
    },
    actions: {
        increaseAction(context) {
            setTimeout(function(){
                //action通过提交mutation改变共享数据状态
                context.commit('increase');
            },3000)
        },
        decrementAction(context){
            setTimeout(function(){
                //action通过提交mutation改变共享数据状态
                context.commit('decrement');
            },3000)
        }
    },
    getters: {
        doubleCounter(state) {
            return state.counter*state.counter
        }
    }
}

export default moduleA


E:\MyStudy\test\VueDemo\src\vuex\moduleB.js


1 const moduleB = {
 2     namespaced: true,
 3     state:{
 4         counter: 5
 5     },
 6     mutations: {
 7         //递增
 8         increase(state) {
 9             console.log("moduleB-increase")
10             state.counter++
11         },
12         //递减
13         decrementAction(state) {
14             state.counter--
15         }
16     },
17     actions: {
18         increaseAction(context) {
19             setTimeout(function(){
20                 //action通过提交mutation改变共享数据状态
21                 context.commit('increase');
22             },3000)
23         },
24         decrementAction(context){
25             setTimeout(function(){
26                 //action通过提交mutation改变共享数据状态
27                 context.commit('decrement');
28             },3000)
29         }
30     },
31     getters: {
32         doubleCounter(state){
33             return state.counter*state.counter
34         }
35     }
36 }
37 
38 export default moduleB


  现在需要在组件中使用mapState和mapGetters

  还是按照之前的套路

  在App.vue组件中访问根根模块store和a模块moduleA的state和getters。

  在Index.vue组件中访问b模块moduleB的state和getters

1.使用mapState

  使用mapState访问state的写法也有多种,我们一个一个来实践(不包含es6的写法)

  [第一种写法]

E:\MyStudy\test\VueDemo\src\App.vue


<template>
  <div id="app">
    <img src="./assets/logo.png">
    <!-- 获取共享数据 -->
    <h1>这里是App组件</h1>
    <h3> App组件获取共享数据 </h3>
    <h3>使用mapState访问根组件counter : {{counter}}</h3>
    <h3>使用mapState访问a组件counter : {{counterA}}</h3>
    <hr/>
    <Index></Index>
  </div>
</template>

<script>
import Index  from './components/Index'
import { mapState } from 'vuex'
export default {
  name: 'App',
  components: { Index },
  computed: mapState({
    //访问store根模块
    counter: function(state){
      return state.counter
    },
    //访问a模块
    counterA: function(state){
      return state.a.counter
    }
  })
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>


E:\MyStudy\test\VueDemo\src\components\Index.vue


<template>
    <div>  
        <h1>这里是Index.vue组件</h1>
        <h3>Index组件获取共享数据 </h3>
        <h3>使用mapState访问b模块counter :{{ counterB }}</h3>
    </div>
</template>
<script>
import { mapState } from 'vuex'
export default {
    name: 'Index',
    computed: mapState({
        counterB: function(state){
            return state.b.counter
        }
    })
}
</script>


  在App.vue、Index.vue组件中使用mapState,首先第一步需要引入mapState

  接着就是在计算属性computed中使用,以Index.vue中的代码为例


computed: mapState({
    counterB: function(state){
         return state.b.counter
    }
})


     可以看到mapState关联到vue的计算属性中。

  获取b模块的state,只需要以vue计算属性的形式在函数中返回state.b.counter即可。

  (获取根模块state返回state.counter;获取a模块state返回state.a.counter)

  这样在模板中就可以使用计算属性的语法访问state

备注:这种方式,当注释掉命名空间的配置后,依然可以正常访问到不同模块的state

  [第二种写法]

  第二种写法和第一种有些类似,只是以字符串的形式返回计算属性。

  我们先在Index.vue组件中访问b模块的数据。

E:\MyStudy\test\VueDemo\src\components\Index.vue


1 <template>
 2     <div>  
 3         <h1>这里是Index.vue组件</h1>
 4         <h3>Index组件获取共享数据 </h3>
 5         <h3>使用mapState访问b模块counter :{{ counterB }}</h3>
 6     </div>
 7 </template>
 8 <script>
 9 import { mapState } from 'vuex'
10 export default {
11     name: 'Index',
12     computed: mapState('b',{
13         counterB: 'counter'
14     })
15 }
16 </script>


  核心代码如下


computed: mapState('b',{
    counterB: 'counter'
})


    可以看到,mapState第一个参数限定了模块名称。

  接着就是以'counter'字符串的形式返回了b模块的counter值。

  那么根据之前一系列的总结,可知

    访问根模块的数据,不需要限定第一个参数;

    访问a模块的数据,需要限定第一个参数为a

  然而,因为访问根模块和访问a模块同在App.vue组件中,那么因为mapState第一个参数限定的问题,我们需要编写两个mapState。

  现在直接上代码(只把computed中的核心代码贴上)

E:\MyStudy\test\VueDemo\src\App.vue


computed: {
    ...mapState({
      //访问store根模块
      counter: 'counter',
    }),
    ...mapState('a',{
      //访问a模块
      counterA: 'counter'
    })
}


  可以看到,我写了两个mapState,还是...mapState的形式。

  ...mapState它是ES6中扩展运算符的语法,应用在mapState上,官方文档是这样说的

   (

    此处若对此有疑问,可以在去仔细研究一下对象扩展运算符的内容。

    我这里贴一个简单的示例

      最终newObj的打印结果为

Vuex实践(下)-mapState和mapGetters_数据

  

    相信这个示例可以很清楚的解释我们写的两个...mapState的写法

  )

  官方文档处提到这个对象展开运算符的场景是为了将一个组件中原本的计算属性和mapState混合使用

  (混合使用这个点日常开发会用到,很实用的一个点)

  那本次我们也是使用这个语法成功的获取到了不同模块的state。

  最后我们在使用浏览器查看一下最终App.vue和Index.vue中的结果

  我们已经使用mapState成功的访问到了多模块中的state数据。

备注:这种关于mapState的写法不能删除moduleA和moduleB中关于命令空间的配置,否则会报错。

  最后作者还尝试了一个问题,就是将moduleA.js中的state属性改为counterA

然后修改了App.vue组件中computed访问a模块数据的代码

最后发现这样并不能正常访问到a模块的state数据。(删除a模块的命名空间配置也无法正常访问)

这个尝试仅给大家一个反面的示例。

2.使用mapGetters

  前面使用mapState访问了state数据,那么现在我们使用mapGetters访问一下vuex中的getters。

  在研究之后发现,暂时发现使用mapGetters访问一下vuex中的getters只有字符串的形式。

E:\MyStudy\test\VueDemo\src\App.vue


1 <template>
 2   <div id="app">
 3     <img src="./assets/logo.png">
 4     <!-- 获取共享数据 -->
 5     <h1>这里是App组件</h1>
 6     <h3> App组件获取共享数据 </h3>
 7     <h3>使用mapState访问根组件counter : {{counter}}</h3>
 8     <h3>使用mapState访问a组件counter : {{counterA}}</h3>
 9     <h3>使用mapGetters访问根组件doubleCounter : {{doubleCounter}}</h3>
10     <h3>使用mapGetters访问a组件doubleCounter : {{doubleCounterA}}</h3>
11     <hr/>
12     <Index></Index>
13   </div>
14 </template>
15 
16 <script>
17 import Index  from './components/Index'
18 import { mapState,mapGetters } from 'vuex'
19 export default {
20   name: 'App',
21   components: { Index },
22   computed: {
23     ...mapState({
24       //访问store根模块
25       counter: 'counter',
26     }),
27     ...mapState('a',{
28       //访问a模块
29       counterA: 'counter'
30     }),
31     ...mapGetters({
32       //访问store根模块
33       doubleCounter: 'doubleCounter'
34     }),
35     ...mapGetters('a',{
36       //访问store根模块
37       doubleCounterA: 'doubleCounter'
38     })
39 
40   }
41   
42 }
43 </script>
44 
45 <style>
46 #app {
47   font-family: 'Avenir', Helvetica, Arial, sans-serif;
48   -webkit-font-smoothing: antialiased;
49   -moz-osx-font-smoothing: grayscale;
50   text-align: center;
51   color: #2c3e50;
52   margin-top: 60px;
53 }
54 </style>


E:\MyStudy\test\VueDemo\src\components\Index.vue


1 <template>
 2     <div>  
 3         <h1>这里是Index.vue组件</h1>
 4         <h3>Index组件获取共享数据 </h3>
 5         <h3>使用mapState访问b模块counter :{{ counterB }}</h3>
 6         <h3>使用mapGetters访问b组件doubleCounter : {{doubleCounterB}}</h3>
 7     </div>
 8 </template>
 9 <script>
10 import { mapState,mapGetters } from 'vuex'
11 export default {
12     name: 'Index',
13     computed: {
14         ...mapState('b',{
15             counterB: 'counter'
16          }),
17         ...mapGetters('b',{
18             doubleCounterB: 'doubleCounter'
19         }),
20     }
21 }
22 </script>


  浏览器查看结果

三.总结

  到此本篇文章基本已经结束了,vuex系列的三篇文章也已经结束。

  关于vuex中大多数的用法基本已经覆盖到,但是还有一些内容没有覆盖到。

  后期攒够一篇在继续更新

  在最后呢,再补充一点,不管是mapState和mapGetters,我们给传入的都是一个字典。

  简单一些的,假如我们的state和getters不重名,我们可以给mapState和mapGetters传入一个数组


1 mapState([
2      'counterA','counterB',...
3 ])
4 
5 mapGetters([
6      'dobuleCounterA','dobuleCounterB',...
7 ])


  这样数组中的字符串元素会直接去映射对应的state和getters。

  (字典形式相当于是将state和getters中的名称在映射过程中进行重命名)