How to teach you to use Vuex

Posted by chris.zeman on Thu, 13 Jan 2022 08:44:26 +0100

Let's briefly introduce vuex. What is this?

First, if you want to know about a technology, go to its official website. It must be true. When you enter the official website, you will see "what vuex is":

As shown in the figure, it is a state management mode in a program. It is a small warehouse that centrally stores the state of all components, and keeps our stored state changing in a predictable way. For predictability, I don't want to explain more now. I believe you will have your own understanding after reading this article.

The first step is to understand Vuex

ūü§Į Imagine a scene

If there are many pages (components / views) in your project and there are multi-level nested relationships between pages, if these pages need to share a state, the following two problems will occur:

  • Multiple views depend on the same state

  • Behaviors from different views need to change the same state

ūü§™ Move your little head and you will come up with a solution to the above methods:

  • For the first problem, if it is a multi-level nested relationship, you can use parent-child components to pass parameters to solve it. Although it is a little troublesome, the good thing is that it can be solved; It's difficult to deal with sibling components or components with more complex relationships. Although it can be solved by various methods, it's really not elegant. Moreover, when the project is bigger, the code will become a shit mountain, which is really annoying.

  • For the second problem, you can change or synchronize multiple copies of the state through direct reference of parent-child components or events. This mode is very fragile, which often makes the code difficult to maintain, and will also turn the code into a shit mountain.

ūüėá At this point, now that I have thought about it, what if I change my mind:

  • Extract the same state that each component needs to rely on, and use the singleton mode for global management.

  • In this mode, any component can directly access this state, or when the state changes, all components are updated.

ūüĎ∂ At this time, Vuex was born!

This is the basic idea behind Vuex, which draws lessons from Flux and Redux. Unlike other patterns, Vuex is a state management library specifically designed for Vue to take advantage of Vue JS fine-grained data response mechanism for efficient state update.

ūüė® Next, you will see the vuex life cycle diagram of the following official website (it doesn't matter if you don't understand it):

ūü§© When should I use vuex?

  • This problem varies from person to person. If you don't need to develop large-scale single page applications, you don't need to use vuex at this time. For example, you have two or three pages. After using vuex, there are more files than your current pages, so it's not necessary.

  • If your project reaches the scale of medium and large applications, you are likely to consider how to better manage the state outside the component, and Vuex will become a natural choice.

ūü§Ē That's all for the brief introduction of vuex. Next, let's use it together!

Step 2: installation

Enter the project, enter the installation instruction in the command line, and press enter

npm install vuex \--save

Then configure vuex to make it work: create the store folder under the src path, and then create the index JS file, the contents of the file are as follows:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        //Define a name for global use
        name: 'Zhang San',
        //Define a number for global use
        number: 0,
        //Define a list for global use
        list: [
            { id: 1, name: '111' },
            { id: 2, name: '222' },
            { id: 3, name: '333' },
        ]
    },
});

export default store;
Copy code

Modify main js:

import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store'; //Introduce the store object we exported earlier

Vue.config.productionTip = false;

new Vue({
    el: '#app',
    router,
    store, //Add the store object to the vue instance
    components: { App },
    template: '<App/>'
});
Copy code

Finally, modify app vue:

<template>
    <div></div>
</template>

<script>
    export default {
        mounted() {
            //Use this$ store. state. XXX has direct access to the status in the warehouse
            console.log(this.$store.state.name); 
        }
    }
</script>
Copy code

At this point, start the project npm run dev to output the value of name we just defined in the store on the console.

  • ūü§Ė Official recommendation 1:

We are officially advised to operate this$ store. state. XXX is best placed in the calculation attribute. Of course, I also suggest you use it to make your code look more elegant, like this:

export default {
    mounted() {
        console.log(this.getName); 
    },
    computed:{
        getName() {
            return this.$store.state.name;
        }
    },
}
Copy code

You can get the same effect as above.

  • ūü§Ė Official recommendation 2:

Do you write this every time$ store. state. XXX annoys you. You really don't want to write this thing. Of course, there are solutions, such as the following:

<script>
import { mapState } from 'vuex'; //Import mapState from vuex
export default {
    mounted() {
        console.log(this.name);
    },
    computed: {
        ...mapState(['name']), //After deconstruction, it is automatically added to the calculation attribute. At this time, it can be accessed directly like the calculation attribute
    },
}
</script>
Copy code

You can even give it an alias and nickname when deconstructing, like this:

...mapState({ aliasName: 'name' }),  //If aliased, the object is received here, not the array
 Copy code

ūü§ó At this point, the installation and initialization of vuex is completed. At this time, you can easily access the state in the warehouse anywhere in the project

Step 3: learn about decorator: Getter

When you see here, it proves that you have perfectly created a vue project and installed vuex in the previous step!

OK! Next, we introduce Getter, a "decorating tool" for reading operations

  • ūü§® Imagine a scenario where you have displayed the name in the store on many pages. At this time, the product manager comes to look for something ūüė°:

  • Product Manager: all name s should be preceded by "hello"!

  • Me: why?

  • Product Manager: do I need to ask why?

  • Me: OK, I'll add it!

At this time, your first thought is how to add it, emm On each page, use this$ store. state. After name obtains the value, it can traverse and append "hello" in front of it.

ūü§¶ūüŹĽ‍‚ôāÔłŹ wrong! This is not good for the following reasons:

  • First, if you use name on all three pages A, B and C, you need to modify all three pages A, B and C. you need to add this method many times for multiple pages, resulting in code redundancy, which is very bad;

  • Second, if the next time the product manager asks you to change "hello" to "fuck", you have to change all three pages again. At this time, you can only smoke your own face

ūüĎŹūüŹĽ Learning from the above lessons, you will have a new idea: we can directly operate or process name in the store to solve the problem from the source! So how should I write it? At this time, the Getter weapon to be introduced this time will shine on the stage!

ūü§° How? No nonsense, show code!

First, add the getters attribute to the store object

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        name: 'Zhang San',
        number: 0,
        list: [
            { id: 1, name: '111' },
            { id: 2, name: '222' },
            { id: 3, name: '333' },
        ]
    },
    //Add getters attribute in the store object
    getters: {
        getMessage(state) { //Get the modified name. The first parameter state is a required parameter and must be written on the formal parameter
            return `hello${state.name}`;
        }
    },
});

export default store;
Copy code

Use in components:

export default {
    mounted() {
        //Note that it is not $store State, but $store getters
        console.log(this.$store.state.name);
        console.log(this.$store.getters.getMessage);
    }
}
Copy code

Then view the console:

No problem

ūü§Ė Official suggestion: do you write this every time$ store. getters. XXX annoys you. You really don't want to write this thing. Of course, there is a solution. The official suggests that we can use mapGetters to deconstruct it into the calculation properties. Just like using mapState, we can directly use this call, as follows:

<script>
import { mapState, mapGetters } from 'vuex';
export default {
    mounted() {
        console.log(this.name);
        console.log(this.getMessage);
    },
    computed: {
        ...mapState(['name']),
        ...mapGetters(['getMessage']),
    },
}
</script>
Copy code

You can get the same effect as before.

Of course, like mapState, you can also take aliases and nicknames, as follows:

...mapGetters({ aliasName: 'getMessage' }),  //If aliased, the object is received here, not the array
 Copy code

ūü§ó OK, when you see here, you have successfully used getters. You can also understand when getters should be used. You can access them through calculation properties (caching) or methods (not caching). You can even call getters methods in getters methods. Of course, you also implement state, Use mapGetters to deconstruct into calculation properties, so you can easily use getters!

ūüėé The operations of reading values include "state" and "getters". Next, we will introduce how to modify values!

Step 4: learn how to modify the value: Mutation

ūü§ó OK! First of all, congratulations on seeing this. So far, we have successfully accessed the values in the store. Next, let me introduce how to modify the values in the state.

  • When it comes to modifying values, some students will think of this:

//Error demonstration
this.$store.state.XXX = XXX;
Copy code

ūü§™ First of all, let me make it clear here: This is the wrong way to write! This is the wrong way to write! This is the wrong way to write!

Why is it wrong? Because the store warehouse is strange, you can take it at will, but you can't change it at will. Let me give an example:

ūü§Ē If you open the wechat circle of friends and see that your friends have sent a message, but there is a typo in the message, what should you do? Can you help him get rid of it? Of course not! We can only inform him to modify it. Because it is someone else's circle of friends, you have no right to operate it. Only he can operate it. Similarly, in vuex, we can't directly modify the value in the warehouse. We must modify it with vuex's own method. At this time, Mutation flashes on the stage!

ūüė¨ After explaining the problem clearly, we are ready to complete an effect: we first output the default value 0 of number in state, and then we change the default value 0 of number in vue component by submitting changes to the value we want to modify, and then output it. In this way, we can simply practice how to use changes. No nonsense, code.

  • Modify store / index js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        name: 'Zhang San',
        number: 0,
    },
    mutations: { //Add nutations attribute
        setNumber(state) {  //Add a changes method. The function of the method is to change num from 0 to 5. state is the default parameter
            state.number = 5;
        }
    },
});

export default store;
Copy code
  • Modify app vue

<script>
export default {
    mounted() {
        console.log(`Old value: ${this.$store.state.number}`);
        this.$store.commit('setNumber');
        console.log(`New value: ${this.$store.state.number}`);
    },
}
</script>
Copy code
  • Run the project and view the console:

  • ūü§° The above is a simple method to implement changes. There is no parameter transfer. What if we want to transfer unfixed parameters? Next, I'll teach you how to solve it

    • Modify store / index js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        name: 'Zhang San',
        number: 0,
    },
    mutations: {
        setNumber(state) {
            state.number = 5;
        },
        setNumberIsWhat(state, number) { //Add a mutations method with parameters
            state.number = number;
        },
    },
});

export default store;
Copy code
  • Modify app vue

<script>
export default {
    mounted() {
        console.log(`Old value: ${this.$store.state.number}`);
        this.$store.commit('setNumberIsWhat', 666);
        console.log(`New value: ${this.$store.state.number}`);
    },
}
</script>
Copy code
  • Run the project and view the console:

no problem!

  • Note: Although the above method of passing parameters can achieve the purpose, it is not recommended. The official recommends passing an object in, which looks more beautiful. You can name the object at will, but we generally name it payload. The code is as follows:

    • Modify store / index js

mutations: {
    setNumber(state) {
        state.number = 5;
    },
    setNumberIsWhat(state, payload) { //Add a mutations method with parameters, and it is officially recommended that payload be an object
        state.number = payload.number;
    },
},
Copy code
  • Modify app vue

<script>
export default {
    mounted() {
        console.log(`Old value: ${this.$store.state.number}`);
        this.$store.commit('setNumberIsWhat', { number: 666 });  //You also need to pass an object when calling
        console.log(`New value: ${this.$store.state.number}`);
    },
}
</script>
Copy code
  • At this point, you can get the same effect as before, and the code is more beautiful!

ūüėĪ Here is an important principle: the functions in variables must be synchronous operations and cannot contain asynchronous operations! (don't worry, we'll talk about asynchrony later)

ūüėĪ Here is an important principle: the functions in variables must be synchronous operations and cannot contain asynchronous operations! (don't worry, we'll talk about asynchrony later)

ūüėĪ Here is an important principle: the functions in variables must be synchronous operations and cannot contain asynchronous operations! (don't worry, we'll talk about asynchrony later)

Well, with this important principle in mind, let's say another tip:

  • ūü§Ė Official suggestion: just like mapState and mapGetters in the beginning, we can use mapMutations in the component instead of this$ store. Is it convenient to commit ('xxx ')?

<script>
import { mapMutations } from 'vuex';
export default {
    mounted() {
        this.setNumberIsWhat({ number: 999 });
    },
    methods: {   //Note that mapMutations are deconstructed into methods instead of calculating attributes
        ...mapMutations(['setNumberIsWhat']),
    },
}
</script>
Copy code
  • At this point, you can get the same effect as before, and the code is a little more beautiful!

    • Of course, you can also call it an alias or nickname, like this:

methods:{
    ...mapMutations({ setNumberIsAlias: 'setNumberIsWhat' }),   //If aliased, the object is received here, not the array
}
Copy code
  • ūü§Ē OK, that's the general introduction to movements. In addition, you have mastered whether there are parameters and how to write them when defining the movements method; After listening to the official suggestions, use mapchanges to deconstruct into the methods inside your component, so that you can easily use the changes method!

  • ūü§™ As mentioned above, movements can only perform synchronous operations, so let's start the next section to see what we should pay attention to when using Actions for asynchronous operations!

Step 5: learn about asynchronous operations: Actions

ūüėÜ OK! In this section, we'll learn how to use Actions. The meaning of Actions is to assume that you have asynchronous operations when modifying the state. The vuex author doesn't want you to put asynchronous operations in Mutations, so he sets up an area for you to put asynchronous operations. This is Actions

ūüėõ Let's go straight to the last code

  • Modify store / index js

const store = new Vuex.Store({
    state: {
        name: 'Zhang San',
        number: 0,
    },
    mutations: {
        setNumberIsWhat(state, payload) {
            state.number = payload.number;
        },
    },
    actions: {   //Add actions attribute
        setNum(content) {  //Add setNum method. By default, the first parameter is content, and its value is a copy of store
            return new Promise(resolve => {  //We simulate an asynchronous operation and change the number to 888 after 1 second
                setTimeout(() => {
                    content.commit('setNumberIsWhat', { number: 888 });
                    resolve();
                }, 1000);
            });
        }
    }
});
Copy code
  • Modify app vue

async mounted() {
    console.log(`Old value: ${this.$store.state.number}`);
    await this.$store.dispatch('setNum');
    console.log(`New value: ${this.$store.state.number}`);
},
Copy code
  • Run the project and view the console:

ūü§ď After reading the example, you can see that an action is to submit a mutation. All asynchronous operations are digested in the action, and then submit a mutation.

ūüėľ Of course, you can imitate mutation to pass parameters, as follows:

  • Modify store / index js

actions: {
    setNum(content, payload) {   //Add payload parameter
        return new Promise(resolve => {
            setTimeout(() => {
                content.commit('setNumberIsWhat', { number: payload.number });
                resolve();
            }, 1000);
        });
    },
}
Copy code
  • Modify app vue

async mounted() {
    console.log(`Old value: ${this.$store.state.number}`);
    await this.$store.dispatch('setNum', { number: 611 });
    console.log(`New value: ${this.$store.state.number}`);
},
Copy code
  • Run the project and view the console

No problem!

  • ūü§Ė Official suggestion 1: if you don't want to use this all the time$ store. To call action in the way of dispatch ('xxx '), you can use mapActions to deconstruct related actions into methods and call directly with this:

<script>
import { mapActions } from 'vuex';
export default {
    methods: {
        ...mapActions(['setNum']),   //Like this, deconstruct into methods
    },
    async mounted() {
        await this.setNum({ number: 123 });  //Just call it like this
    },
}
</script>
Copy code

Of course, you can also take aliases and nicknames, as follows:

...mapActions({ setNumAlias: 'setNum' }),   //If aliased, the object is received here, not the array
 Copy code
  • ūü§Ė Official suggestion 2: in store / index In actions in JS, the formal parameters of the method can directly deconstruct the commit, which can facilitate subsequent operations:

actions: {
    setNum({ commit }) {   //Directly remove the content structure and deconstruct the commit. You can call it directly below
        return new Promise(resolve => {
            setTimeout(() => {
                commit('XXXX');  //Direct call
                resolve();
            }, 1000);
        });
    },
},
Copy code
  • ūü§† OK, see here, you should understand the position of action in vuex. When to use action and when not to use it, you must have your own judgment. The main judgment condition is whether the operation I want to do is asynchronous, which is also the essence of action. Of course, you should not confuse action with mutation. Action is actually the upper level of mutation. After some asynchronous operations are processed in action, the later modification of state is left to mutation.

Step 6, summary

ūü§ó OK! That's all for the general explanation of vuex. You must be familiar with vuex here. You will install it, configure it, read the state value, or even modify the getter, and then you will modify the value (Mutation). If you have asynchronous operations and need to modify the state, you need to use Action, so that you can use vuex in your project! Come on! ūü§Ē

Topics: Front-end Vue