Eleven: Vuex, getters, mapState, mapGetters, mapActions, mapMutations, multi-component shared data, vuex modularization

一.vuex

1.vuex introduction

What is it?

vuex is a vue plug-in that implements centralized state (data) management in Vue. Centralized is like a teacher teaching a group of students, and the state is data;

Vuex centrally manages (read and write) the shared data of multiple components in Vue applications, and is suitable for communication between any components.

When to use it?

When multiple components depend on the same state; behaviors from different components need to change the same state (data)

How vuex works:

2.Vuex use

1. Install vuex:npm i vuex@3
Note that Vue2 must install vuex3. If it is vue3, you can directly install npm i vuex to install vuex4.

2. Create the store(vuex)/index.js file:

Introduce the plug-in in this file and use the vuex plug-in. The vuex plug-in must be used before the store is introduced. If vuex is introduced and used in main.js, because all the import statements in the js file will be promoted to the beginning of execution, an error will be reported. drop. Summary: The store must be introduced after Vue.use(Vuex)

//This file is used to create a store
//Introduce vue
import Vue from 'vue'
//Introduce Vuex
import Vuex from 'vuex'
Vue.use(Vuex)
// Prepare actions - used to respond to actions in components
const actions = {}
// Prepare mutations - used to manipulate data (state)
const mutations = {}
// Prepare state-for storing data
const state = {}


// Create an exposed store and manage actions, mutations, and state
export default new Vuex.Store({
    actions,
    mutations,
    state,
})
//Expose store
// export default store

3.Introduce store into main.js

import Vue from 'vue'
import App from './App.vue'

//Introduce plugin
// import vueResource from 'vue-resource'

//Introduce store
import store from './store/index'
Vue.config.productionTip = false

// use plugin
// Vue.use(VueResource)


newVue({
    el: '#app',
    render: h => h(App),
    store: store,//or write store directly
    //Install the global event bus to transfer data from list to search
    beforeCreate() {
        Vue.prototype.$bus = this
    },
})

This exposes the store so that both vc and vm can use $store.

3.vuex summation case

First, give the initial data to the store’s state:

//Prepare state-used to store data
const state = {
    sum: 0, //current sum
}

Then use interpolation syntax to display the sum on the page:

 <h1>The current sum is: {<!-- -->{ $store.state.sum }}</h1>

Add addition method:

Use dispatch to pass the callback function to actions (order dishes). If it is just simple addition and subtraction, and there is no logic such as adding odd numbers, you can also use commit directly and pass it directly to mutations for direct addition and subtraction;

 methods: {
    increment() {
      this.$store.dispatch("jia", this.n);
      // Use commit to directly talk to vc and mutations, skipping actions
      // this.$store.commit("JIA", this.n);
    },

Then use the jia function in actions to receive (process customer needs)

Then use commit to transfer the data to mutations: equivalent to the waiter telling the chef the customer’s order;

The data context carried in it is equivalent to the mini version of $Store, which can call the commit in the body. The value is the number selected by the user and the data transmitted by the component.

 // value is the number selected by the user, context is minniStore, including commit
   jia: function (context, value) {
       console.log('jia in action was called', context, value);
        context.commit('JIA', value)
        },

Use the JIA function in mutations to receive the passed data (receive the menu). Generally, the function in actions is capitalized. Mutations are awesome!

The first parameter is the state object, and the second parameter is the passed data. Implement data addition

Prepare mutations - used to operate data (state)
const mutations = {
    JIA: function (state, value) {
        console.log('JIA of mutations was called', state, value);
        state.sum + = value
    },

Finally, the matching setter in the state is used to implement responsiveness and update the data to the page (serving food).

Store/index.js:

//This file is used to create a store
//Introduce vue
import Vue from 'vue'
//Introduce Vuex
import Vuex from 'vuex'
Vue.use(Vuex)
// Prepare actions - used to respond to actions in components
const actions = {
    // value is the number selected by the user, context is minniStore, including commit
    jia: function (context, value) {
        console.log('jia in action was called', context, value);
        context.commit('JIA', value)
    },
    jian: function (context, value) {
        console.log('jian in action was called', context, value);
        context.commit('JIAN', value)
    },
    jiaOdd: function (context, value) {
        console.log('jiaOdd in action was called', context, value);
        if (context.state.sum % 2) {
            context.commit('JIA', value)
        }
    },
    jiaWait: function (context, value) {
        console.log('jiaWait in action was called', context, value);
        setTimeout(() => {
            context.commit('JIA', value)
        }, 500);
    }

}
// Prepare mutations - used to manipulate data (state)
const mutations = {
    JIA: function (state, value) {
        console.log('JIA of mutations was called', state, value);
        state.sum + = value
    },
    JIAN: function (state, value) {
        console.log('JIAn of mutations was called', state, value);
        state.sum -= value
    },

}
// Prepare state-for storing data
const state = {
    sum: 0, //current sum
}

// Create an exposed store and manage actions, mutations, and state
export default new Vuex.Store({
    actions,
    mutations,
    state,
})

Count.vue:





Points to note:

Generally speaking, network requests or other business logic are written into actions.
Data can also be manipulated in actions, but if the data is not manipulated in mutations but in actions, the vuex developer tool will be ineffective;

2. Configuration items

1.getters configuration items

You can use getters to process the data in the state, similar to the computed property in Vue.

Configure getters in the store:

.......
// Prepare state-for storing data
const state = {
    sum: 0, //current sum
}
// Prepare getters - used to process data in state
const getters = {
    bigSum(state) {
        return state.sum * 10
    }
}
// Create an exposed store and manage actions, mutations, and state
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})

Then read directly in the component: $store.getters.bigSum:

 <h3>The current sum is magnified ten times: {<!-- -->{ $store.getters.bigSum }}</h3>

2.mapState and mapGetters

First import the mapState and mapGetters methods from vuex: import { mapState, mapGetters, } from “vuex”;

When we use interpolation syntax to obtain the state value, we can only use $store.state.xxx, or $store.getters.xxx;

For example, this kind of thing is more complicated.

<h1>The current sum is: {<!-- -->{ $store.state.sum }}</h1>
<h1>The current sum magnified ten times is: {<!-- -->{ $store.getters.bigSum }}</h1>
<h1>I study {<!-- -->{ $store.state.subject }} at {<!-- -->{ $store.state.school }}</h1>

This can be simplified using computed properties:

computed: {
 
     sum: function () {
      return this.$store.state.sum;
    },
    school: function () {
      return this.$store.state.school;
    },
    subject: function () {
      return this.$store.state.subject;
    },
     bigSum: function () {
      return this.$store.getters.bigSum;
    },
  },

This allows you to use simple interpolation syntax directly:

 <h1>The current sum is: {<!-- -->{ sum }}</h1>
    <h3>The current sum is magnified ten times: {<!-- -->{ bigSum }}</h3>
    <h3>I am in {<!-- -->{ school }}, studying {<!-- -->{ subject }}</h3>

However, this has poor reusability, so vuex provides mapState and mapGetters to generate calculated properties and read data:

The key corresponds to the calculated attribute method name, and the value corresponds to the data name in state. If the method name is the same as the data name, it can be abbreviated in array form, …is the spread operator

 computed: {
    // Use MapState to generate calculated properties and read data from state (object writing method)
    // ...refers to expanding and displaying the data in the mapState object
  ...mapState({ he: "sum", xuexiao: "school", xueke: "subject" }),
    // When the calculated attribute name is the same as the data name in state, you can use array writing
    ...mapState(["sum", "school", "subject"]),

    // Generate calculated properties with MapGetters, read data from state, object writing method
    // ...mapGetters({ bigSum: "bigSum" }),
    //Use MapGetters to generate calculated attributes, read data from state, and write arrays
    ...mapGetters(["bigSum"]),
  },

3.mapMutations and mapActions

First import the mapMutations and mapActions methods from vuex: import { mapMutations, mapActions } from “vuex”;

In the past, when vc directly transferred data to actions or mutations, it needed to use dispatch or commit in methods.

For example: this.$store.commit(“JIA”, this.n); or this.$store.dispatch(“jia”, this.n);

But now mapMutations and mapActions can use the corresponding methods generated by them to contact mutations and actions.

 methods: {
    // Manual writing method
    /* increment() {
      this.$store.dispatch("jia", this.n);
      // Use commit to directly talk to vc and mutations, skipping actions
      // this.$store.commit("JIA", this.n);
    },
    decrement() {
      this.$store.dispatch("jian", this.n);
      // Use commit to directly talk to vc and mutations, skipping actions
      // this.$store.commit("JIAN", this.n);
    }, */

    // With the corresponding method generated by mapMutation, commit will be called in the method to contact mutations (object writing method)
    ...mapMutations({ increment: "JIA", decrement: "JIAN" }),
    // With the corresponding method generated by mapMutation, commit will be called in the method to contact mutations (array writing method)
    // ...mapMutations(['JIA','JIAN']),

    //************************************************ ****************
    // Manual writing method
    /* incrementOdd() {
      this.$store.dispatch("jiaOdd", this.n);
    },
    incrementWait() {
      this.$store.dispatch("jiaWait", this.n);
    }, */

    // With the corresponding method generated by mapActions, dispatch will be called in the method to contact actions (object writing method)
    ...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiaWait" }),
    // With the corresponding method generated by mapActions, dispatch will be called in the method to contact actions (array writing method)
    // ...mapActions(["jiaOdd", "jiaWait"]),
  },

In addition, mapMutations and mapActions do not have passed parameters, and parameters need to be passed when the template is bound to the event.

 <!-- Add n to pass parameters, otherwise the event object mouse action will be passed -->
    <button @click="increment(n)"> + </button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">Add when the sum is odd</button>
    <button @click="incrementWait(n)">Wait and then add</button>

3. Multiple components share data

Each component can be used with each other through configuration. For example, the count component above can detect how many people there are in the component below, and the component below can detect the sum of the components above;

person.vue:

<template>
  <div>
    <h1>Personnel list</h1>
    <h3>The sum of count components is: {<!-- -->{ sum }}</h3>
    <input type="text" placeholder="Please enter your name" v-model="name" />
    <button @click="add">Add</button>
    <ul>
      <li v-for="p in personList" :key="p.id">{<!-- -->{ p.name }}</li>
    </ul>
  </div>
</template>

<script>
import { mapState } from "vuex";
import { nanoid } from "nanoid";
export default {
  name: "Person",
  data() {
    return {
      name: "",
    };
  },
  computed: {
    personList() {
      return this.$store.state.personList;
    },
    sum() {
      return this.$store.state.sum;
    },
    // You can also write through mapState
    // ...mapState(["sum", "personList"]),
  },
  methods: {
    add() {
      const personObj = { id: nanoid(), name: this.name };
      this.$store.commit("ADD_PERSON", personObj);
      this.name = "";
    },
  },
};
</script>

Count.vue:



Four.vuex modularity + namespaced namespace

Because there are many different actions in response to different components in actions, there are many different methods of operating data in mutations, and there are many data belonging to different components in state, so you can use vuex modularization to manage different data methods separately;

Make the code easier to maintain and make the classification of various data more clear.

You can write both to the index.js of the store, or you can write different js files in the store separately. For example, the count.js and person.js files are written separately below.

count.js:

Note: Only after the namespace namespaced: true is turned on can the names of countAbout and personAbout be recognized.

//Sum related configurations
export default {
    // The countAbout name can only be recognized if the namespace is true.
    namespaced: true,
    actions: {
        jiaOdd: function (context, value) {
            console.log('jiaOdd in action was called', context, value);
            if (context.state.sum % 2) {
                context.commit('JIA', value)
            }
        },
        jiaWait: function (context, value) {
            console.log('jiaWait in action was called', context, value);
            setTimeout(() => {
                context.commit('JIA', value)
            }, 500);
        }
    },
    mutations: {
        JIA: function (state, value) {
            console.log('JIA of mutations was called', state, value);
            state.sum + = value
        },
        JIAN: function (state, value) {
            console.log('JIAn of mutations was called', state, value);
            state.sum -= value
        },
    },
    state: {
        sum: 0, //current sum
        school: 'Shanhe University',
        subject: 'front-end',
    },
    getters: {
        bigSum(state) {
            return state.sum * 10
        }
    }
}

person.js:

// Personnel management related configurations
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
    namespaced: true,
    actions: {
        //Add a person named Wang
        addPersonWang(context, value) {
            if (value.name.indexOf('王') === 0) {
                context.commit('ADD_PERSON', value)
            } else {
                alert('Please add a person named Wang')
            }
        },
        // Contact the backend server to ask for a name: send an axios request to the backend
        addPersonServer(context) {
            axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
                Response => {
                    context.commit('ADD_PERSON', { id: nanoid(), name: Response.data })
                },
                error => {
                    alert(error.message)
                }
            )
        }
    },
    mutations: {
        //Add a person
        ADD_PERSON: function (state, value) {
            console.log('ADD_PERSON of mutations was called', state, value);
            state.personList.unshift(value)
        }
    },
    state: {
        personList: [
            { id: '001', name: 'huahua' }
        ]
    },
    getters: {
        firstPersonName(state) {
            return state.personList[0].name
        }
    }
}

Then you need to introduce js in index.js and expose it

//Create an exposed store,
export default new Vuex.Store({
    modules: {
        countAbout: countOptions,
        personAbout: personOptions
    }

1. After opening the namespace, read the state data

Methods can be added to computed

 personList() {
      return this.$store.state.personAbout.personList;
    },

You can also read data through mapState in computed, but you need to mark where the data comes from and add a parameter name;

For example, sum and school are data from countAbout.

...mapState("countAbout", ["sum", "school", "subject"]),
...mapState("personAbout", ["personList"]),

2. After opening the namespace, read the getters data

First add data in getters

 getters: {
        firstPersonName(state) {
            return state.personList[0].name
        }
    }

Then you can add method acquisition in computed. You can use square brackets to add parameter names here;

 firstPersonName() {
      return this.$store.getters["personAbout / firstPersonName"];
    },

It can also be read using mapGetters:

 ...mapGetters("countAbout", ["bigSum"]),

3. After opening the namespace, call dispatch

You can directly call the methods in actions in personAbout through dispatch in maths:

 addPersonServer() {
      this.$store.dispatch("personAbout/addPersonServer");
    },

You can also call the methods in actions in countAbout through mapActions in methods:

...mapActions("countAbout", {incrementOdd: "jiaOdd",incrementWait: "jiaWait",}),

4. After opening the namespace, call commit

You can use commit directly in the methods method to call the methods in mutations in personAbout:

 methods: {
    add() {
      const personObj = { id: nanoid(), name: this.name };
      this.$store.commit("personAbout/ADD_PERSON", personObj);
      this.name = "";
    },

You can also use mapMutations directly,

...mapMutations("countAbout", { increment: "JIA", decrement: "JIAN" }),

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Vue entry skill tree Home page Overview 39182 people are learning the system