Vuex’s state, mapState,…mapState, getters, mapGetters,…mapGetters

I found the following code in the project:

computed: {
  ...mapGetters([
    'permission_routes',
    'sidebar'
  ]),
  activeMenu() {
    const route = this.$route
    const { meta, path } = route
    if (meta.activeMenu) {
      return meta.activeMenu
    }
    return path
  },
}

Among them, what is the syntax of …mapGetters? I looked up the information to understand, it is the syntax of Vuex.

Then I learned about several concepts of Vuex, state, mapState and…mapState.

1. State

There are many similarities between vuex’s state and vue’s data, both of which are used to store some data or state values. These values will realize the two-way binding between the mounted data and the dom, that is, when you change the value, you can trigger the update of the dom.

When used, state is generally mounted to the computed property of the subcomponent, which facilitates timely response to the subcomponent when the value of the state changes.

let state = {
  count: 1,
  name: 'lyh',
  sex: 'male',
  from: 'china'
}
export default state
<template>
  <div id="example">
    <button @click="decrement">-</button>
    {<!-- -->{count}}
    {<!-- -->{dataCount}}
    <button @click="increment"> + </button>
  </div>
</template>
<script>
export default {
  data () {
    return {
      dataCount: this.$store.state.count //Receive with data
    }
  },
  computed:{
    count(){
      return this.$store.state.count //Receive with computed
    }
  }
  methods: {
    increment () {
      this.$store.commit('increment')
    },
    decrement () {
      this.$store.commit('decrement')
    }
  }
}
</script>

The result is that the value received using data cannot respond to updates in time, so computed can be used.

2. mapState

mapState is syntactic sugar for state.

Before using mapState, you need to import this helper function.

import { mapState } from 'vuex'

The usage is

<template>
  <div id="example">
    <button @click="decrement">-</button>
    {<!-- -->{count}}
    {<!-- -->{dataCount}}
    <button @click="increment"> + </button>
    <div>{<!-- -->{sex}}</div>
    <div>{<!-- -->{from}}</div>
    <div>{<!-- -->{myCmpted}}</div>
  </div>
</template>
<script>
import { mapState } from 'vuex'
export default {
  data () {
    return {
      str: 'Nationality',
      dataCount: this.$store.state.count
    }
  },
  computed: mapState({
    count: 'count', // first way of writing
    sex: (state) => state.sex, // second way of writing
    from: function (state) { // Use ordinary function this to point to the vue instance, please pay attention
      return this.str + ':' + state.from
    },
    // Note that the writing below looks the same as the above. In fact, the this pointer of the arrow function does not point to the vue instance, so do not abuse the arrow function.
    // from: (state) => this.str + ':' + state.from
    myCmpted: function () {
      // No state is needed here, test the original usage of computed
      return 'test' + this.str
    }
  }),
  methods: {
    increment () {
      this.$store.commit('increment')
    },
    decrement () {
      this.$store.commit('decrement')
    }
  },
  created () {
    //Write a timer and find that computed is still maintained. As long as the relevant internal attributes change (whether it is a change in the current instance data or a value change in vuex), the dom and value updates will be triggered.
    setTimeout(() => {
      this.str = 'Country'
    }, 1000)
  }
}
</script>

When used, computed receives the return value of the mapState function. You can receive the value in the store in three ways. Please see the comments for details.

In fact, the second and third methods are the same, except that the former uses the lazy syntax of ES6 – arrow function. When lazy, you should pay attention to one issue, the pointing problem of this pointer.

We should not use arrow functions in Vue for laziness, which will lead to many errors that are difficult to detect. If you use state and also need to use this of the current Vue instance, be sure to use conventional writing.

Of course, computed will not lose its original function because of the introduction of the mapState auxiliary function – it is used to expand the current vue data, but the writing method will be a little strange.

If you have written a lot of computed properties and find out halfway through that you need to introduce vuex and want to use the convenience of mapState auxiliary functions, you can do the following things.

//Previous computed
computed:{
    fn1(){ return ...},
    fn2(){ return ...},
    fn3(){ return ...}
    ........
}
//After introducing the mapState auxiliary function
 
computed:mapState({
    //Copy and paste first
    fn1(){ return ...},
    fn2(){ return ...},
    fn3(){ return ...}
    ...
    //Maintain vuex again
    count:'count'
    .......
})

We hope that we can use mapState directly instead of doing some useless copy-and-paste operations, hoping that it can be automatically integrated into the current production environment.

ES6+ (or ES7) provides this convenience.

3….mapState

In fact… mapState is not an extension of mapState, but… an extension of the object expander.

In this way, we can use mapState freely.

//Previous computed
computed:{
    fn1(){ return ...},
    fn2(){ return ...},
    fn3(){ return ...}
    ........
}
//After introducing the mapState auxiliary function
 
computed:{
    //Keep the original one
    fn1(){ return ...},
    fn2(){ return ...},
    fn3(){ return ...}
    ...
    //Maintain vuex again
    ...mapState({ //The... here is not an ellipsis, it is an object expander
        count:'count'
    })
}

After learning state, mapState and…mapState, we can continue to learn the concepts of getters, mapGetters,…mapGetters.

4. getters

Vuex provides a state unified management tree such as state. You can use computed properties in Vue to receive these public states for use. Of course, you can also make some changes to this value based on receiving the original value, such as

computed:{
  sex:function(){
      return this.$store.state.sex + 'Add a string, it's a transformation'
  }
}

But if your other components also use this transformation method to modify this value, then you may have to copy and paste this function into other components.

Of course, in order to solve this problem, vuex itself provides a method similar to calculating properties. Getters allow you to derive some new states from the store’s state.

Of course, if multiple components don’t need to use this state, or if each sub-component uses different derived properties, then you don’t need getters at all.

//state.js
let state = {
  from: 'china',
  arr: [2, 3, 1, 4, 6]
}
export default state
//getters.js
// The first parameter is state
let address = (state) => {
  return 'Nationality:' + state.from
}
//The second parameter can access getters
let addressMore = (state, getters) => {
  return 'other description' + getters.address
}
// Return a function. This function can pass parameters. Of course, this function will eventually return a specific value.
//In this example, this method is used to query whether a certain value exists in the arr array in state
let findArr = (state) => (number) => {
  let ifExit = state.arr.find((n) => n === number) // arr.find is an extension of arrays in ES6 syntax
  if (typeof (ifExit) === 'undefined') {
    return false
  } else {
    return true
  }
}
export {address, addressMore, findArr}
<template>
  <div>
    <div>{<!-- -->{from}}</div>
    <div>{<!-- -->{from2}}</div>
  </div>
</template>
 
<script>
// import { mapGetters } from 'vuex'
export default {
  computed: {
    from: function () {
      return this.$store.getters.address
    },
    from2: function () {
      return this.$store.getters.addressMore
    }
  },
  created () {
    console.log(this.$store.getters.findArr(2))
    console.log(this.$store.getters.findArr(7))
  }
}
</script>

The result is:

5. mapGetters auxiliary function

Similar to mapState, it can aid understanding.

<template>
  <div>
    <div>{<!-- -->{from}}</div>
    <div>{<!-- -->{from2}}</div>
  </div>
</template>
 
<script>
import { mapGetters } from 'vuex'
export default {
  computed: mapGetters({
      'from': 'address',
      'from2': 'addressMore',
      'find': 'findArr'
  }),
  created () {
    console.log(this.find(1)) // Since the getters have been mounted to the current instance through computed, you do not need to access it through the this.$store.getters method.
    console.log(this.$store.getters.findArr(2))
    console.log(this.$store.getters.findArr(7))
  }
}
</script>

6….mapGetters

<template>
  <div>
    <div>{<!-- -->{from}}</div>
    <div>{<!-- -->{from2}}</div>
  </div>
</template>
 
<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters({
      'from': 'address',
      'from2': 'addressMore',
      'find': 'findArr'
    })
  },
  created () {
    console.log(this.find(1)) // Since the getters have been mounted to the current instance through computed, you do not need to access it through the this.$store.getters method.
    console.log(this.$store.getters.findArr(2))
    console.log(this.$store.getters.findArr(7))
  }
}
</script>

In many cases, getters are not used. Please use them as needed. Do not use getters to manage all derived states of state. Only consider using getters if there are multiple sub-components or sub-pages that need to be used.