How to use Vuex deep analysis in uni-app

This article takes an in-depth look at Vuex, a Vue.js state management library. We’ll cover the problem it was created to solve, the core concepts behind it, how to set it up, and of course, use code examples every step of the way.

Vuex is a state management library built by the Vue team for managing data in Vue.js applications. It provides a way to centrally manage data used across applications and allows easy read and write operations.

Why it?-Vuex

Vue advocates breaking down views into components. These components are reusable Vue instances that can accept data, methods, etc. Data is where the view state is saved, and methods are what allow us to manipulate that state based on user interaction on the view.

When the user clicks a button in the component, a method is called, which in turn performs an action on the state, which in turn updates the view about the change.

However, sometimes multiple components need to share a state, or after the state in one component is modified, you need the parent/child or sibling component to perform subsequent operations.

Depending on the location of the second component, you may decide to use the or keyword to directly access the data or methods of the second component and perform the above operations. But what if you had to do this for as many components as possible? propsthis.$parent

As your projects get larger, you’ll find yourself passing properties everywhere and manipulating the DOM directly to access various components.

This approach becomes very tedious and also makes the code base difficult to maintain or debug when you encounter bugs. This is where Vuex shines. It provides a global scope where you can place all state that will be shared among various components.

It also gives our code more structure, makes debugging easier since we can use developer tools to track errors that occur, and of course provides the responsiveness that Vue brings. Think of it like windows in JavaScript – every component has access to it

Install Vuex

npm install vuex --save

This will install the latest version of Vuex in your project. Once completed, we need to create our store.js file using the following code to initialize Vuex in the Vue application;

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

Vue.use(Vuex)

Now we can go ahead and create a store. The store is essentially a reactive object that stores the application’s state, getters, mutations, and actions.

Understanding Store

A store is essentially a centralized state, and it has some core concepts that allow us to achieve this centralization. These concepts include:

  1. State
  2. Getters
  3. Mutations
  4. Actions

State

This is an object that contains the entire data. This is similar to the keyword in a single-component structure, except that this state can be accessed from multiple components, and when this state is updated, all components that access it also receive this change. To create this object, we do the following

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

//Install the Vuex plug-in into vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ''
    }
})

To access Vuex state in our Vue component, we must first import the store into the component by creating a computed property that returns that state, and then render that state into the view.

Now, let’s import the store. There are two main methods:

  1. Manually import the store in each component where you will use Vuex state, as follows:
<template>
    <main>
        <h1>Cart Content</h1>
        <p>{<!-- -->{cartValue}}</p>
    </main>
</template>

<script>
// Import Vuex Store into Component
import store from 'store.js';
export default {
    computed: {
        cartValue() {
            // Return Vuex state from store
            return store.state.cart;
        }
    }
}
</script>

2. Globally inject the Vuex store into the Vue instance, which automatically allows us to use

import Vue from 'vue';
import store from './store.js';

newVue({
    // Adding the Vuex store to the Vue instance
    store,
}).$mount('#app');
<template>
    <main>
        <h1>Cart Content</h1>
        <p>{<!-- -->{cartValue}}</p>
    </main>
</template>

<script>
export default {
    computed: {
        cartValue() {
            // Accessing the Vuex state
            return this.$store.state.cart;
        }
    }
}
</script>

Getter

Getters are pretty much computed properties stored by Vuex. They allow us to generate a new state based on the current state – for example, calculating how many items are in a shopping cart.

It also helps reduce duplication of code, ideally multiple components require this data and we usually have to operate in each component. With getters, we can do it once and reference it anywhere.

To create a getter, we do the following:

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

//Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
})

Next, we access the getter from our Vue component via:

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {<!-- -->{totalNumberOfCartItems}}</p>
    </main>
</template>

<script>
export default {
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    }
}
</script>

Now, whenever an item is added to the cart, the total number of items in the cart is automatically updated.

Mutations

Mutation is the only way we can update Vuex state. They perform only one task: setting a state. It is a function with two parameters, state and payload, where payload is not required.

The payload is simply the data used to update the state. Mutations are synchronous, so we cannot perform asynchronous tasks in them.

Now, let’s add a mutation to the code:

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

//Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        //Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
    },
})

Next, we need to update the state from our Vue component, and to do this we need to submit a mutation.

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {<!-- -->{totalNumberOfCartItems}}</p>
        <form @submit.prevent="addItemToCart">
            <input type="text" v-model="item" required>
            <button type="submit">Add to cart</button>
        </form>
    </main>
</template>

<script>
export default {
    data() {
        return {
            item: ''
        }
    },
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    },
    methods: {
        addItemToCart() {
            // Check that the input field isn't empty
            if(this.item !== '') {
                // committing the additemtocart mutation with the payload
                this.$store.commit('addItemToCart', this.item)
            }
        }
    }
}
</script>

Now, whenever the user enters a value in the input box and clicks the submit button, the item is added to the cart and the total number of items in the cart is updated in the view.

Actions

Actions are similar to mutations, but they commit mutations rather than changing state. They are asynchronous and therefore allow us to perform asynchronous tasks; when these tasks complete, we proceed to submit a mutation, which in turn updates the state.

In order to demonstrate the operation, we will continue to submit the shopping cart items to the API.

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

//Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        //Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
        // Clear items in the cart
        emtpyCart (state) {
            state.cart = [];
        }
    },
    
    actions: {
        checkout({commit}, requestObject) {
            // API Call to submit the items in the cart
            Vue.http.post('submit', requestObject).then((response) => {
                // log success
                console.log(response);
                // Clear Cart by mutating the state
                commit('emptyCart');
            }).catch((error) => {
                // log error
                console.log(error);
            }
        }
    }
})

Looking at the code above, we have created an action called action which accepts two things checkout

Commit: allows us to call the Commit method in actions

requestObject: allows us to pass data to the action

Entering the action, we make an asynchronous call to the API and then pass it to the API. Upon success, we log the response and then proceed to clear the cart state, but first we have to create a mutation whose only task is to clear the cart state. requestObjectemptyCart

Now that we have seen how to create an action, we move on to triggering the action. In order to trigger an action, Vuex provides us with a commanddispatch

this.$store.dispatch(‘actionName’, payload);

Let’s add an action to the code and dispatch it from the view:

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {<!-- -->{totalNumberOfCartItems}}</p>
        <form @submit.prevent="addItemToCart">
            <input type="text" v-model="item" required>
            <button type="submit">Add to cart</button>
        </form>
        
        <button type="button" @click="checkout">Checkout</button>
    </main>
</template>

<script>
export default {
    data() {
        return {
            item: ''
        }
    },
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    },
    methods: {
        addItemToCart() {
            // Check that the input field isn't empty
            if(this.item !== '') {
                // committing the additemtocart mutation with the payload
                this.$store.commit('addItemToCart', this.item)
            }
        },
        
        checkout() {
            // Make sure cart is not empty
            if(this.totalNumberOfCartItems > 0 ) {
                //create request
                let requestPayload = { cart: this.$store.state.cart };
                // Dispatch the action
                this.$store.dispatch('checkout', requestPayload);
            }
            else {
                alert('Cart is empty');
            }
        }
    }
}
</script>

Based on the code above, we create a checkout button in the view and create a checkout method to check whether the shopping cart is empty before trying to schedule the action of submitting the item.

This works, but something is missing. You may be wondering what this is? We’ve been able to dispatch an action, but we don’t know if the action was successful.

Did the API call fail? Did it pass? How do I get this information to notify the user? Actions can handle Promise or return a Promise.

Modify the example code to return a Promise:

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

//Install the Vuex plugin on vue
Vue.use(Vuex);

// create a Vuex store instance
export const store = new Vuex.Store({
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        //Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
        // Clear items in the cart
        emtpyCart (state) {
            state.cart = [];
        }
    },
    
    actions: {
        checkout({commit}, requestObject) {
            return new Promise((resolve, reject) => {
                
                // API Call to submit the items in the cart
                Vue.http.post('submit', requestObject).then((response) => {
                    // log success
                    console.log(response);
                    // Clear Cart by mutating the state
                    commit('emptyCart');
                    // return success
                    resolve(response);
                }).catch((error) => {
                    // log error
                    console.log(error);
                    // return error
                    reject(error);
                }
            })
        }
    }
})

Now we can use the returned value to update the view’s state as follows:

<template>
    <main>
        <h1>Cart Content</h1>
        <p>Total Number of Items: {<!-- -->{totalNumberOfCartItems}}</p>
        <form @submit.prevent="addItemToCart">
            <input type="text" v-model="item" required>
            <button type="submit">Add to cart</button>
        </form>
        
        <button type="button" @click="checkout">Checkout</button>
    </main>
</template>

<script>
export default {
    data() {
        return {
            item: ''
        }
    },
    computed: {
        totalNumberOfCartItems() {
            // Accessing the Vuex state
            return this.$store.getters.totalNumberOfCartItems;
        }
    },
    methods: {
        addItemToCart() {
            // Check that the input field isn't empty
            if(this.item !== '') {
                // committing the additemtocart mutation with the payload
                this.$store.commit('addItemToCart', this.item)
            }
        },
        
        checkout() {
            // Make sure cart is not empty
            if(this.totalNumberOfCartItems > 0 ) {
                //create request
                let requestPayload = { cart: this.$store.state.cart };
                // Dispatch the action
                this.$store.dispatch('checkout', requestPayload).then((response) => {
                    // Alert Response from API
                    alert(response);
                }).catch((error) => {
                    // Alert Error from API
                    alert(error);
                });
            }
            else {
                alert('Cart is empty');
            }
        }
    }
}
</script>

Actions also allow you to dispatch multiple actions (that is, one action can dispatch one or more other actions). All you have to do is pass a parameter, and then you can dispatch other actions within your action.

checkout({ dispatch, commit }, requestObject) {
    // dispatch an action
    dispatch('actionName');

    // dispatch another action
    dispatch('actionName2', request);
};

Add structure to Store

Now, all our state, getters, mutations and actions are in one file, the store.js file. Depending on the size of our codebase, this file can get quite large, so it makes sense for us to split it into separate files.

store/
--| store.js
--| state.js
--| getters.js
--| mutations.js
--| actions.js

Now, our store looks like this:

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions
})

Modules

Vuex also provides us with modules that we can further build or break our store into smaller store modules. Each module has its own state, getters, mutations and actions.

It works by grouping related states, getters, mutations and operations into a module. It is very useful when we have a large application and the store contains a lot of code.

To refactor our store into a module, we will create a file called cart.js and proceed to break down all the states, mutations, and actions related to the cart as follows:

// import Vue
import Vue from 'vue';

export default {
    state: {
        cart: ["bread", "rice", "beans", "turkey"]
    },
  
    getters: {
        // Fetch the total number of items in the cart
        totalNumberOfCartItems: state => {
            return state.cart.length;
        },
    },
    
    mutations: {
        //Add item to cart
        addItemToCart (state, payload) {
            state.cart.push(payload);
        },
        // Clear items in the cart
        emtpyCart (state) {
            state.cart = [];
        }
    },
    
    actions: {
        checkout({commit}, requestObject) {
            return new Promise((resolve, reject) => {
                
                // API Call to submit the items in the cart
                Vue.http.post('submit', requestObject).then((response) => {
                    // log success
                    console.log(response);
                    // Clear Cart by mutating the state
                    commit('emptyCart');
                    // return success
                    resolve(response);
                }).catch((error) => {
                    // log error
                    console.log(error);
                    // return error
                    reject(error);
                }
            })
        }
    }
}

Next, we import it and register it with our main store.

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions,
  modules: {
      cart
  }
})

In the end, our code structure looks like this:

store/
--| store.js
--| state.js
--| getters.js
--| mutations.js
--| actions.js
--| modules/
    --| cart.js

Summary

uex creates a store, which consists of state, getters, mutations, and actions. To update or change a state, you must submit a mutation.

To perform an asynchronous task, you need an operation. The action is dispatched, and upon success, a mutation is submitted, which changes a state and thus updates the view.

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