As front-end applications become more and more complex, state and data management becomes the key to building large-scale applications. Inspired by projects such as Redux, the Vue.js team also tailored the state management library, Vuex. In this tutorial, we will familiarize you with the three key concepts of Store, Mutation and Action, and update the front end code of the Mini-Mall application.

Welcome to the Series from Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express:

  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (PART 1)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (PART 2)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Part 3)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Part 4)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (5)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (6)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (7)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (Final)

If you think we wrote well, remember to like + follow + comment three times, encourage us to write a better tutorial 💪

Use Vuex for state management

We covered the basics of Vue in Articles 1 and 3, and with this knowledge you can already implement some relatively simple applications. However, for complex applications, such as components with more than three levels of nesting, the previous knowledge can be difficult to handle. Fortunately, the Vue community has created a state management container for us, Vuex, to handle data and state management for large applications.

Install Vuex dependencies

First we open the command line, go to the project directory, execute the following command to install Vuex:

npm install vuex
Copy the code

Create Vuex Store

Vuex is a front-end state management tool that aims to take over Vue’s state and allow Vue to focus on rendering pages. It is similar to creating a “database” on the front end and storing all the front-end state in this “database”. This “database” is just a normal JavaScript object.

Ok, now that we’ve talked about what Vuex is, let’s look at how Vuex can be used in Vue. The “database” created by Vuex is usually referred to by the term Store. Usually we create a separate store folder for store related content. Create a store folder in SRC and create an index.js file in it.

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

Vue.use(Vuex);

export default new Vuex.Store({
  strict: true.state: {
    // bought items
    cart: [],
    // ajax loader
    showLoader: false.// selected product
    product: {},
    // all products
    products: [
      {
        name: '1',}],// all manufacturers
    manufacturers: [],
  }
})
Copy the code

The above code can be divided into three parts.

  • So first we importVue å’Œ Vuex
  • And then we callVue.useMethod that tells Vue we will useVuexThis is the same as we used beforeVue.use(router)The same principle
  • And then finally we deriveVuex.StoreInstance, and passed instrict å’Œ stateParameters. herestrictParameter representation, we must use the Mutation function of Vuex to changestateOtherwise, an error will be reported (Mutation is covered in the “State Management with Vuex” section). whilestateParameters are used to store our global state, as defined herecart,showLoaderAnd other attributes are the data we need to improve the content of the application later.

Integrate Vuex and Vue

Once we have created and exported a Store instance of Vuex, we are ready to use it. Open the SRC /main.js file, import the previously created store at the beginning, and add store to the Vue initialization parameter list as follows:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import { ValidationProvider } from 'vee-validate';

import App from './App';
import router from './router';
import store from './store';

Vue.config.productionTip = false;
Vue.component('ValidationProvider', ValidationProvider);

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'});Copy the code

As you can see in the file above, we first import the store instance we defined in SRC /store/index.js. Then, when the Vue instance is initialized, we add the store instance to the parameter list using the object property shorthand.

When we pass store to Vue for initialization, Vue injects the store state into all Vue components, so that all Vue components share the same global state, which is essentially a JS object. All state changes in the application operate on state and then trigger a rerendering of the component in a responsive manner, so state is also referred to as “the only source of truth for data”.

This saves the state in a global JavaScript object called State, and then all the adding, deleting, modifying, and checking is done on this JavaScript object. This allows us to avoid the complexity of transferring properties between components when the nesting level is too deep. The modifications are intuitive, making it easy to develop large applications and collaborate with teams.

View the effect of Vuex integration

Now that Vuex and Vue are integrated, let’s look at what Vuex does, but first let’s talk about computed properties.

Computed attributes (Computed)

First we added a script section, and then we added a computed property to the exported object, which declares complex expressions that might be used in the template. Let’s look at an example of computed properties:

We might want to fetch some data from a multilevel nested object in a template, or we might want to render data that needs to be evaluated by a complex expression. For example, we want to render obj1.obj2.obj3.a + obj1.obj4.b, which looks like this in the template:

<template>
<div>
  {{ obj1.obj2.obj3.a + obj1.obj4.b }}
</div>
</template>
<script>
export default {
  data: {
    obj1: {
      obj2: {
        obj3: {
          a
        }
      },
      obj4: {
        b
      }
    }
  }
}
</script>
Copy the code

As you can see at a glance, this template has such a complex expression that it’s hard to figure out what it’s rendering, which makes the code very unreadable, so Vue provides us with computed attributes that represent complex expression results with simple variables. To simplify the interpolation in the template, and make our template look more readable, the above code would look like this when modified with computed attributes:

<template>
<div>
  {{ addResult }}
</div>
</template>

<script>
export default {
  data: {
    obj1: {
      obj2: {
        obj3: {
          a
        }
      },
      obj4: {
        b
      }
    }
  },
  computed: {
    addResult() {
      return this.obj1.obj2.obj3.a + this.obj1.obj4.b
    }
  }
}
</script>
Copy the code

As you can see, when we use the calculation property addResult, we simplify the way we write in the template and see what we are rendering.

After understanding the calculation attributes, we open the SRC/pages/admin/Products. The vue, the following improvement of the content to see Vuex and vue integration effect:

<template>
  <div>
    <div class="title">
      <h1>This is Admin</h1>
    </div>
    <div class="body">
      {{ product.name }}
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    product() {
      return this.$store.state.products[0]; }}}</script>
Copy the code

As can be seen, the above content improvement is mainly divided into two parts:

  • So first we defined aproductCompute property, which returns a value fromstoreStored in thestatePick up toproductsThe first element of the array, notice that when we look at the section “Integrating Vuex and Vue”, we willstoreAs a Vue initialization instance parameter, so we can pass in all Vue componentsthis.$store.stateIs stored in Vuex Storestate.
  • Next we use computed propertiesproductAnd got itnameProperty to render.

summary

In this section, we learned how to integrate Vuex into Vue:

  • First we installed itvuexRely on
  • And then we hadsrcHere we createstoreFolder, used to save Vuex related content, instoreUnder the file, we createdindex.jsFile, instantiated in thereVuex.StoreClass, we pass two arguments during instantiation:strict å’Œ state.strictMeans we tell Vue that onlyMutationMethods can be modifiedstateTo ensure the uniqueness of the modified state;stateIt’s the state of our entire application, the state of our entire application is getting from it, and all changes to the state of our entire application are modifying it. So thisstateThere is also the term “the only true source of data”.
  • And then we go through themain.jsImport instantiatedstore, add it to the parameter list of initializing Vue instance, realize the integration of Vuex and Vue.
  • Finally, we looked at computed properties, and then we looked at how to get in computed propertiesthis.$store.stateShows the effect of Vuex integration.

Now that we have integrated Vuex and captured the state stored in the Vuex Store in the Vue component, let’s look at how to change this state.

Modify local state using Mutation

In the previous section we defined the Vuex Store and stored the global state in it. In this section we will learn how to modify this state.

Understand Mutation: The only means of modifying a state

Vuex provides us with Mutation, which is the only means of modifying the saved state in the Vuex Store.

Mutation is a set of (state, payload) => newState functions defined in the Vuex Store Mutation attribute in response to events or actions emanating from the Vue view layer, such as:

ACTION_NAME(state, payload) {
  // Operate on 'state' to return a new 'state'
  return newState;
}
Copy the code

ACTION_NAME is used to specify the name of the event or action sent from the view layer. This function accepts two parameters, state and payload. State is the state stored in the Vuex Store. Payload is the parameter of the event or action that is responded to. Then we manipulate the existing state with the parameters of the payload and return the new state. In this way, we can respond to modify the global state stored in the Vuex Store.

Now that we know the concept of Mutation, let’s look at how to use it.

Initialization state (hard coded)

Open the SRC /store/index.js file, change the state and add mutations as follows:

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

Vue.use(Vuex);

export default new Vuex.Store({
  strict: true.state: {
    // bought items
    cart: [],
    // ajax loader
    showLoader: false.// selected product
    product: {},
    // all products
    products: [
      {
        _id: '1'.name: 'iPhone'.description: 'iPhone is a series of smart phones developed by Apple Inc., which is powered by the iOS operating system developed by Apple Inc. '.image: 'https://i.gadgets360cdn.com/large/iPhone11_leak_1567592422045.jpg'.price: 2000.manufacturer: 'Apple Inc'
      },
      {
        _id: '2'.name: 'glory 20'.description: 'li xian's 48 million ultra wide Angle AI four shots 3200W beauty selfie kirin Kirin980 full netcom version 8GB+128GB blue water jade full screen mobile phone'.image: 'https://article-fd.zol-img.com.cn/t_s640x2000/g4/M08/0E/0E/ChMlzF2myueILMN_AAGSPzoz23wAAYJ3QADttsAAZJX090.jpg'.price: 2499.manufacturer: 'huawei'
      },
      {
        _id: '3'.name: 'MIX2S'.description: 'Snapdragon 845 Full Screen NFC Game Smart Camera Phone White Full Netcom 6+128'.image: 'http://himg2.huanqiu.com/attachment2010/2018/0129/08/39/20180129083933823.jpg'.price: 1688.manufacturer: 'millet'
      },
      {
        _id: '4'.name: 'IQOO Pro'.description: '12GB+128GB Racing Black Qualcomm Snapdragon 855Plus mobile phone 48 million AI Triple camera 44W Super flash Charge 5G Full Netcom mobile phone '.image: 'https://www.tabletowo.pl/wp-content/uploads/2019/08/vivo-iqoo-pro-5g-blue-1.jpg'.price: 4098.manufacturer: 'Vivo'
      },
      {
        _id: '5'.name: 'Reno2'.description: '48 million zoom quad 8+128G Anti-shake 6.5-inch full screen New deep-sea noctilucent (8GB+128GB).image: 'https://news.maxabout.com/wp-content/uploads/2019/08/OPPO-Reno-2-1.jpg'.price: 2999.manufacturer: 'OPPO'}].// all manufacturers
    manufacturers: [],
  },
  mutations: {
    ADD_TO_CART(state, payload) {
      const { product } = payload;
      state.cart.push(product)
    },
    REMOVE_FROM_CART(state, payload) {
      const { productId } = payload
      state.cart = state.cart.filter(product= >product._id ! == productId) } } });Copy the code

As you can see, the code improvements above are divided into two parts:

  • First we expandedstateIn theproductsProperty to hold the initial data of our mini-e-commerce platform, which we hard-coded into the code. In the next section, “Using Action to Get Remote Data,” we will dynamically get data from the back-end server.
  • And then we hadVuex.StoreOne is added to the instantiated parametermutationsProperty, where two functions are definedADD_TO_CART å’Œ REMOVE_FROM_CARTRepresents actions initiated from the view layer to add and remove items from the cart, respectively.

Create the ProductList component

Then create a SRC/components/products/ProductList. Vue file, it is a commodity list components, used to display the details of the goods, the code is as follows:

<template>
  <div>
    <div class="products">
      <div class="container">
        This is ProductList
      </div>
      <template v-for="product in products">
        <div :key="product._id" class="product">
          <p class="product__name">Product name: {{product.name}}</p>
          <p class="product__description">Introduction: {{product. The description}}</p>
          <p class="product__price">Price: {{product. Price}}</p>
          <p class="product.manufacturer">Manufacturer: {{product.manufacturer}}</p>
          <img :src="product.image" alt="" class="product__image">
          <button @click="addToCart(product)">Add to shopping cart</button>
        </div>
      </template>
    </div>
  </div>
</template>

<style>
.product {
  border-bottom: 1px solid black;
}

.product__image {
  width: 100px;
  height: 100px;
}
</style>

<script>
export default {
  name: 'product-list'.computed: {
    // a computed getter
    products() {
      return this.$store.state.products; }},methods: {
    addToCart(product) {
      this.$store.commit('ADD_TO_CART', { product }); }}}</script>
Copy the code

Let’s start with the script part of this component:

  • First, we define a calculated propertyproductsThrough thethis.$store.state.productsObtained from the local stateproductsArray and as a calculated propertyproductsThe return value of the
  • It then defines a click eventaddToCart, and passed in the currently activeproductParameters. Triggered when the user clicks “Add Shopping cart”addToCartEvents, which are emitted by the view layer above. This is throughthis .$store.commitThe object that will carry the current commodity{product}Submit as payload to type asADD_TO_CARTthemutation, in themutationTo make local state changes, which we’ll pull out latermutationsSee the detailed operation in the file.

Looking at the template section of this component, the products array obtained locally is iterated with V-for, and the details of each product object are displayed in the template. In addition, we added an “Add to Cart” button at the end of each product object information, allowing us to add specified items to the cart.

Add data to the page

With the Store and components in place, we can add data to the previous page. SRC /pages/ home.vue

<template>
  <div>
    <div class="title">
      <h1>In Stock</h1>
    </div>
    <product-list></product-list>
  </div>
</template>

<script>
import ProductList from '@/components/products/ProductList.vue';
  export default {
    name: 'home',
    data () {
      return {
        msg: 'Welcome to Your Vue.js App'
      };
    },
    components: {
      'product-list': ProductList
    }
  }
</script>
Copy the code

As you can see, after importing the ProductList component, we registered it with Components and then used this component in the template.

Then modify the shopping Cart page SRC /pages/ cart.vue file to display the items in the shopping Cart. Add the following code:

<template>
  <div>
    <div class="title">
      <h1>{{msg}}</h1>
    </div>
    <template v-for="product in cart">
      <div :key="product._id" class="product">
        <p class="product__name">Product name: {{product.name}}</p>
        <p class="product__description">Introduction: {{product. The description}}</p>
        <p class="product__price">Price: {{product. Price}}</p>
        <p class="product.manufacturer">Manufacturer: {{product.manufacturer}}</p>
        <img :src="product.image" alt="" class="product__image">
        <button @click="removeFromCart(product._id)">Remove from shopping cart</button>
      </div>
    </template>
  </div>
</template>

<style>
.product {
  border-bottom: 1px solid black;
}

.product__image {
  width: 100px;
  height: 100px;
}
</style>

<script>
  export default {
    name: 'home',
    data () {
      return {
        msg: 'Welcome to the Cart Page'}},computed: {
      cart() {
        return this.$store.state.cart; }},methods: {
      removeFromCart(productId) {
        this.$store.commit('REMOVE_FROM_CART', { productId }); }}}</script>
Copy the code

We added two main parts of code to this component:

  • First, in the Script section, we added a calculated property and a click event. Cart: this.$store.state. Cart: this.$store.state. Cart: this. The removeFromCart event is triggered when the user clicks on an item in the cart to remove it from the cart, and the id of the item to be removed is passed as an argument, Next, use this. codestore.mit to commit an object containing productId as a payload to a mutation of type REMOVE_FROM_CART, where local state changes are performed. The detailed modification operation can be seen in the mutations file extracted later.

  • Next comes the Template section, where we walk through the shopping cart array with V-for, displaying all the items in the shopping cart in the template. At the end of each item information, a remove cart button is added, which triggers the removeFromCart event when the user wants to remove specified items from the cart.

See the effect

Run NPM start in the root directory of the project and enter the development server to see the result:

As you can see, our shopping cart is empty at first, then we randomly select two phones, click “Add to Shopping cart”, and then we can see it on the shopping cart page! We can also remove items from the shopping cart.

summary

In this section we learned how to initiate notifications that change local state:

  • The first thing we need to do isVuex.StoreAdd a parameter to the instantiated parametermutationsProperty to which to add the corresponding method, for exampleADD_TO_CART å’Œ REMOVE_FROM_CART.
  • We then need to initiate “notifications” with different user actions (such as clicking add cart or remove cart) to passthis.$store.commitCommits the object to be manipulated as a payload to the corresponding type (i.eADD_TO_CART å’Œ REMOVE_FROM_CART)mutation, in themutationTo modify the local status.

Use Action to get remote data

In the previous section, we learned how to initiate “notifications” of local state changes at the view layer. In this section, we’ll learn how to get remote data from the back end. Request library we used axios to install dependencies with the following command:

npm install axios
Copy the code

Understand Action: asynchronous operation

Vuex provides us with Action, which is used for asynchronous operation. We can initiate network data request from the back end here and submit the requested data to the corresponding mutation.

Action is a set of methods defined in the Action property of the Vuex Store that respond to events or actions distributed from the Vue view layer. An Action is a function of the form (context, payload) => response.data:

productById(context, payload) {
  // Perform an asynchronous operation to fetch remote data from the back end and return it
  return response.data;
}
Copy the code

Among them:

  • The function nameproductByIdThe name used for events or actions that should be dispatched from the view layer
  • The function takes two argumentscontext å’Œ payload
  • contextRefers to theactionThe context, andstoreInstances have the same methods and properties, so we can call themcontext.commitTo submit amutation, or throughcontext.state å’Œ context.gettersIn order to getstate å’Œ getters, butcontextThe object is notstoreThe instance itself
  • payloadIt’s the parameters that we carry when we distribute, and then we pass throughpayloadTo retrieve the back-end response data and return it. This allows us to synchronously update back-end data based on user actions and submit back-end response data tomutationAnd then usemutationPerform local data update.

Implement the first Action

Let’s strike while the iron is hot and implement the first Action. Go to the SRC /store/index.js file again and change the code as follows:

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

const API_BASE = 'http://localhost:3000/api/v1';

Vue.use(Vuex);

export default new Vuex.Store({
  strict: true.state: {
    // bought items
    cart: [],
    // ajax loader
    showLoader: false.// selected product
    product: {},
    // all products
    products: [],
    // all manufacturers
    manufacturers: [],
  },
  mutations: {
    ADD_TO_CART(state, payload) {
      const { product } = payload;
      state.cart.push(product)
    },
    REMOVE_FROM_CART(state, payload) {
      const { productId } = payload
      state.cart = state.cart.filter(product= >product._id ! == productId) }, ALL_PRODUCTS(state) { state.showLoader =true;
    },
    ALL_PRODUCTS_SUCCESS(state, payload) {
      const { products } = payload;

      state.showLoader = false; state.products = products; }},actions: {
    allProducts({ commit }) {
      commit('ALL_PRODUCTS')

      axios.get(`${API_BASE}/products`).then(response= > {
        console.log('response', response);
        commit('ALL_PRODUCTS_SUCCESS', {
          products: response.data, }); }}}}));Copy the code

As you can see, we did the following:

  1. To import theaxiosAnd definedAPI_BASEBack-end interface root route;
  2. We are instoreTo remove the previous hard – coded fake data, so thatproductsThe default is an empty array;
  3. Then, inmutationsPropertyALL_PRODUCTS 和 ALL_PRODUCTS_SUCCESSMethod to respond toactionEvents of the corresponding type submitted in;ALL_PRODUCTS 将 state.showLoaderSet totrue, display the loading status;ALL_PRODUCTS_SUCCESS 将 actionThe submitted data is saved tostateAnd cancel the loading state.
  4. And finally addedactionsProperties,actionsProperty is defined inallProductsFunction to respond to the corresponding type of event distributed by the view layer; We first submitted a type ofALL_PRODUCTS çš„ mutationAnd then inaxiosSubmit the request after it succeedsALL_PRODUCTS_SUCCESSAnd the supplementaryproductsData (payload)

prompt

Const {commit} = context (context.mit) const {commit} = context (context.mit);

Update the ProductList component

In the SRC/components/products/ProductList. Vue file, we made modification, to its main added created life cycle function, at the beginning of the component is created first determines whether there is goods in the local products, If not, make a network request to the back end to get the data. The code is as follows:

<template>
  <div>
    <div class="products">
      <div class="container">
        This is ProductList
      </div>
      <template v-for="product in products">
        <div :key="product._id" class="product">
          <! -- Other fields -->
          <p class="product.manufacturer">Manufacturer: {{product. The manufacturer. The name}}</p>
          <img :src="product.image" alt="" class="product__image">
          <button @click="addToCart(product)">Add to shopping cart</button>
        </div>
      </template>
    </div>
  </div>
</template>

<! -- style -->

<script>
export default {
  name: 'product-list',
  created() {
    if (this.products.length === 0) {
      this.$store.dispatch('allProducts')}},computed: {
    // ...
  },
  methods: {
    // ...}}</script>
Copy the code

Notice that we changed two things:

  • Adjust the “manufacturer” field in the template to{{product.manufacturer}}Modified to{{product.manufacturer.name}}
  • addcreatedLifecycle method, which is determined when the component is first createdthis.products.length === 0 是 trueorfalseIf it istrueIf there is no commodity in the local, it needs to obtain commodity data at the back end, so it passesthis.$store.dispatchThe mode trigger type isallProducts çš„ action, in theactionIn the asynchronous operation, initiated network request back-end request commodity data and return; If it isfalseProves that the item exists locally, so it can be fetched directly from the local and rendered.

Finally, we also need to adjust the “manufacturer” field in SRC /pages/ cart.vue by modifying the template code as follows:

<template>
  <div>
    <div class="title">
      <h1>{{msg}}</h1>
    </div>
    <template v-for="product in cart">
      <div :key="product._id" class="product">
        <! -- Other fields -->
        <p class="product.manufacturer">Manufacturer: {{product. The manufacturer. The name}}</p>
        <img :src="product.image" alt="" class="product__image">
        <button @click="removeFromCart(product._id)">Remove from shopping cart</button>
      </div>
    </template>
  </div>
</template>

<! -- style -->

<! -- script -->
Copy the code

Also the {{product. The manufacturer}} is modified to {{product. The manufacturer. The name}}.

See the effect

Before testing the effects of this step, make sure MongoDB and the back-end API server are enabled. In the meantime, if you haven’t tested it before in this second tutorial, there is a good chance that your database is empty, download our supplied MongoDB JSON data files, manufacturers. JSON and products.json, and run the following command:

mongoimport -d test -c manufacturers manufacturers.json
mongoimport -d test -c products products.json
Copy the code

Then enter the front end test, you should be able to see the data from the background, and then add to the shopping cart!

summary

In this section we learned how to use Action to get remote data and submit the obtained data to the corresponding Mutation:

  • First we need to import the dependencies:axios å’Œ API_BASE, due to initiating network request.
  • Secondly, we need to be in thestoreAdd to instanceactionsAttribute, and inactionsProperty defines methods that respond to events of the corresponding type that are distributed by the view layer.
  • Make different network requests in different ways, whether you need to fetch data from the back end, modify the back end, etc. The data result of the back-end response is then submitted to the corresponding typemutationIn the.

In the next tutorial, we’ll explore Vue componentalization further to simplify page logic and extract Getters and Mutation data logic.

Want to learn more exciting practical skills tutorial? Come and visit the Tooquine community.