Componentalization and logic reuse can help you write simple, easy-to-understand code. As applications get more complex, it becomes necessary to separate repetitive logic in the view layer into components that can be reused across multiple pages. At the same time, on the Vuex side, the logic in the Store becomes more and more bloated, so it is necessary to use Getters provided by Vuex to reuse the local data retrieval logic. In this tutorial, we will take you through the process of extracting Vue components to simplify page logic and reuse local data retrieval logic using Vuex Getters.

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 (4)
  • From Zero to Deployment: Implementing Mini Full Stack E-commerce Applications with Vue and Express (part 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 💪

Simplify page logic with Vue components

In previous tutorials, we have learned how to use Vuex for state management, how to use Action to get remote data, and how to use Mutation to modify local state, which allows users to modify client data simultaneously, update backend data synchronically, then update local data, and finally re-render.

In this section we will take the idea of Vue componentization one step further to simplify complex page logic.

Implement ProductButton component

We open the SRC/components/products/ProductButton vue file, it is used in the operating state of goods in the shopping cart button component, the code is as follows:

<template> <div> <button v-if="isAdding" class="button" @click="addToCart"> </button> <button V-else class="button" </button> </div> </template> <script> export default {props: ['product'], computed: { isAdding() { let isAdding = true; this.cart.map(product => { if (product._id === this.product._id) { isAdding = false; }}); return isAdding; }, cart() { return this.$store.state.cart; } }, methods: { addToCart() { this.$store.commit('ADD_TO_CART', { product: this.product, }) }, removeFromCart(productId) { this.$store.commit('REMOVE_FROM_CART', { productId, }) } } } </script>Copy the code

This component determines whether to create the Add or remove button from the cart by v-if determining whether isAdding is true. The CART array is obtained locally via this.$store.state.cart. In isAdding we set it to true, and then use the Map method of the CART array to evaluate whether the item is currently in the cart. If not, isAdding is true and the add button is created. If yes isAdding is false, create the Remove from Cart button.

The corresponding buttons add two click events: addToCart and removeFromCart

  • Triggered when the Add to cart button is clickedaddToCartWe passedthis.$store.commitTo submit the object containing the current item as a payload directly to typeADD_TO_CART çš„ mutationTo add the item to the local shopping cart.
  • Triggered when the Remove from Shopping cart button is clickedremoveFromCartAnd so did wethis.$store.commitTo submit an object containing the current commodity ID as a payload directly to typeREMOVE_FROM_CARTthemutationTo remove the item from the local shopping cart.

Implement the ProductItem component

SRC/components/products/ProductItem. Vue file for commodity information components, used to show the product details, and registered the above button component, change the state of goods in the shopping cart, in addition we also use the previously created good ProductButton components, The realization of goods in the shopping cart state to modify.

  • First of all byimport ProductButton from './ProductButton'Import createdProductButtonComponents.
  • Then, incomponentsRegistered component in.
  • Finally, use this component in the template.

The code is as follows:

<div> <div class="product"> <p class="product__name"> {{product. Name}}</p> <p class="product__description"> {{product.price}}</p> <p class="product.manufacturer"> {{product.manufacturer.name}}</p> <img :src="product.image" alt="" class="product__image"> <product-button :product="product"></product-button> </div> </div> </template> <script> import ProductButton from './ProductButton'; export default { name: 'product-item', props: ['product'], components: { 'product-button': ProductButton, } } </script>Copy the code

As you can see, we present the product object passed in by the parent component to the template and pass it to the child component ProductButton.

Refactor the ProductList component

With ProductButton and ProductItem, reconstructing the before we can slightly swollen ProductList components, modify SRC/components/products/ProductList. Vue, the code is as follows:

// ...
        This is ProductList
      </div>
      <template v-for="product in products">
        <product-item :product="product" :key="product._id"></product-item>
      </template>
    </div>
  </div>
// ...
</style>

<script>
import ProductItem from './ProductItem.vue';
export default {
  name: 'product-list',
  created() {
    // ...
      return this.$store.state.products;
    }
  },
  components: {
    'product-item': ProductItem
  }
}
</script>
Copy the code

This part of the code is to display the product information of the previous logical code into the child component ProductItem, and then import and register the child component ProductItem, and then mount the child component into the template.

As you can see, we get the Products array locally via this.$store.state.products and return it to the calculated property Products. We then use V-for to traverse the Products array in the template and pass each Product object to each child component ProductItem, displaying the corresponding product information in each child component.

Refactoring Cart components

Finally, we reconstructed a wave of shopping Cart components SRC /pages/ cart.vue, and also used a child component ProductItem to simplify the page logic, modifying the code as follows:

// ...
      <h1>{{msg}}</h1>
    </div>
    <template v-for="product in cart">
      <product-item :product="product" :key="product._id"></product-item>
    </template>
  </div>
</template>
 // ...
</style>

<script>
import ProductItem from '@/components/products/ProductItem.vue';
  export default {
    name: 'home',
    data () {
      // ...
        return this.$store.state.cart;
      }
    },
    components: {
      'product-item': ProductItem
    }
  }
</script>
Copy the code

Here, too, the child ProductItem is first imported and registered, and then mounted in the template. Cart gets the cart array locally as this.$store.state.cart and returns it to the compute property CART. In the template, v-for traverses the shopping cart array and passes each item object in the shopping cart to the corresponding child ProductItem, which displays the corresponding item information.

Open the item and look at the list of items. You can see that the “Add to Cart” button has been added under each item:

In the shopping cart, there is also a “Remove cart” button:

Buy, buy, buy!

summary

In this section we learned how to use Vue components to simplify page logic:

  • First we need to passimportTo import child components.
  • Then, incomponentsRegister child components in.
  • Finally, mount the child component into the template and pass the data that needs to be displayed by the child component.

Reuse local data acquisition logic using Vuex Getters

In this section, we will implement the product details page for the e-commerce application. The logic of data acquisition is very consistent between commodity details and the previous commodity list. Can we not write duplicate code? The answer is yes. In this section, we use Vuex Getters to duplicate the local data acquisition logic.

Vuex allows us to define “getters” (you can think of them as computed properties of the store) in the store. Just like evaluating properties, the return value of a getter is cached based on its dependency and is recalculated only if its dependency value changes.

Getters are also a set of methods defined in the Getter property of the Vuex Store to get data in the local state. We can access getters in two ways, one through properties and the other through methods:

  • Property accessThe way tothis.$store.getter.allProducts, the correspondinggetterAs follows:
allProducts(state) {
    // Return local data
    return state.products;
}
Copy the code
  • Methods to accessThe way tothis.$store.getter.productById(id), the correspondinggetterAs follows:
productById: (state, getters) = > id => {
      // Perform a series of operations with the id parameter passed in and return local data
      return state.product;
  }
Copy the code

We can see that getters accept two parameters: state and getters. State represents the local data source. We can get different getter properties with the second parameter getters.

Define Vuex Getters

All talk, no fake handle. Let’s masturbate some getters. Open the SRC /store/index.js file and add the action attribute, mutation attribute, and getters, the hero of this section. The code is as follows:

// ...

      state.showLoader = false;
      state.products = products;
    },
    PRODUCT_BY_ID(state) {
      state.showLoader = true;
    },
    PRODUCT_BY_ID_SUCCESS(state, payload) {
      state.showLoader = false;

      const{ product } = payload; state.product = product; }},getters: {
    allProducts(state) {
      return state.products;
    },
    productById: (state, getters) = > id => {
      if (getters.allProducts.length > 0) {
        return getters.allProducts.filter(p= > p._id == id)[0];
      } else {
        returnstate.product; }}},actions: {
    // ...
      commit('ALL_PRODUCTS')

      axios.get(`${API_BASE}/products`).then(response= > {
        commit('ALL_PRODUCTS_SUCCESS', {
          products: response.data,
        });
      })
    },
    productById({ commit }, payload) {
      commit('PRODUCT_BY_ID');

      const { productId } = payload;
      axios.get(`${API_BASE}/products/${productId}`).then(response= > {
        commit('PRODUCT_BY_ID_SUCCESS', {
          product: response.data, }); }}}}));Copy the code

There are three main parts added here:

  • inactionsAdd theproductByIdProperty when the view layer assigns an ID to typePRODUCT_BY_IDtheaction, where an asynchronous operation is performed to get the specified item from the back end and commit the item to the corresponding typemutationIn, we come to the next step.
  • inmutationsAdd thePRODUCT_BY_IDandPRODUCT_BY_ID_SUCCESSProperty to save the submitted goods locally in response to events of the specified type.
  • addedgettersAnd in thegettersAdd theallProductsProperties andproductByIdMethod to get local data. inallProductsTo get all the goods in the local; inproductByIdLooks for the presence of the local item by the id passed in, returns the item if it exists, and returns an empty object if it does not.

Use Getters in the backend Products component

Let’s start with a simple example to demonstrate how to use Vuex Getters. Open the backstage commodity components, SRC/pages/admin/Products. The vue, we call the corresponding getter properties by means of attribute access, thus obtaining local goods, the code is as follows:

/ /... export default { computed: { product() { return this.$store.getters.allProducts[0]; }}} //...Copy the code

Us through this. $store. Getters. AllProducts property access way to invoke the corresponding getter allProducts attribute, the first of an array and returns the local goods.

Create the ProductDetail component

Then begin to implement commodity details component SRC/components/products/ProductDetail vue, the code is as follows:

<template>
  <div class="product-details">
    <div class="product-details__image">
      <img :src="product.image" alt="" class="image">
    </div>
    <div class="product-details__info">
      <div class="product-details__description">
        <small>{{product.manufacturer.name}}</small>
        <h3>{{product.name}}</h3>
        <p>
          {{product.description}}
        </p>
      </div>
      <div class="product-details__price-cart">
        <p>{{product.price}}</p>
        <product-button :product="product"></product-button>
      </div>
    </div>
  </div>
</template>

<style>
  .product-details__image .image {
    width: 100px;
    height: 100px;
  }
</style>

<script>
import ProductButton from './ProductButton';
export default {
  props: ['product'],
  components: {
    'product-button': ProductButton
  }
}
</script>
Copy the code

This component displays the product object passed in by the parent component in a template and reuses the ProductButton component.

Add a link in the ProductItem component

With the product details, we also need a link to the details. Again into SRC/components/products/ProductItem. Vue file, we have to modify, commodity information in the template to use vue native components router – the link up, realized commodity information may click to view details. The code is as follows:

<template> <div> <div class="product"> <router-link :to="'/detail/' + product._id" class="product-link"> <p {{product.name}}</p> <p class="product__description"> {{product.description}}</p> <p class="product__price"> {{product.manufacturer.name}}</p> <img :src="product.image" alt="" class="product__image"> </router-link> <product-button :product="product"></product-button> </div> </div> </template> <style> .product { border-bottom: 1px solid black; } .product__image { width: 100px; height: 100px; } </style> <script> import ProductButton from './ProductButton'; export default { // ...Copy the code

After the modification of this component, clicking any information of the commodity will trigger the route to jump to the commodity detail page and transfer the commodity ID to the detail page through dynamic routing.

Use Getters in ProductList

Modify the commodity list component SRC/components/products/ProductList. Vue file, use the Vuex Getters reuse the local data access logic, the code is as follows:

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

<script>
import ProductItem from './ProductItem.vue';
export default {
  // ...
  computed: {
    // a computed getter
    products() {
      return this.$store.getters.allProducts;
    }
  },
  components: {
    // ...
Copy the code

When calculating the attribute in the products we use this $store. The getters. AllProducts allProducts attributes of calling getters property access way, we also know that in the corresponding getter access to local products in the array.

Create the Detail page component

After the ProductDetail sub-component is implemented, we can build the product details page component SRC /pages/ detail.vue as follows:

<template> <div> <product-detail :product="product"></product-detail> </div> </template> <script> import ProductDetail from '@/components/products/ProductDetail.vue'; Export default {created() {const {name} = this.product; if (! name) { this.$store.dispatch('productById', { productId: this.$route.params['id'] }); } }, computed: { product() { return this.$store.getters.productById(this.$route.params['id']); } }, components: { 'product-detail': ProductDetail, } } </script>Copy the code

This component defines a calculated property called product that returns the item specified in the local state. . Here we use the enclosing $store getters. ProductById (id) method to access the specified way for local goods, the id parameter here by this. $route. The params [‘ id ‘] obtained from the current active route objects, And passes it into the corresponding getter to get the specified item locally.

When the component is created, determine whether the item exists locally. If not, send the object containing the current item ID as the payload to the productById action through this.$store.dispatch. Then commit to the corresponding mutation for local state modification, which has made us accustomed to the idea.

Configure the route on the Detail page

Finally, we opened the route configuration file SRC /router/index.js, imported the Detail component, and added the corresponding route parameters as follows:

// ...

import Home from '@/pages/Home';
import Cart from '@/pages/Cart';
import Detail from '@/pages/Detail';

// Admin Components
import Index from '@/pages/admin/Index';
// ...
      name: 'Cart'.component: Cart,
    },
    {
      path: '/detail/:id'.name: 'Detail'.component: Detail,
    }
  ],
});
Copy the code

When it comes to acceptance, run the project and click on a single product, you can enter the page of product details, and the data is completely consistent:

summary

In this section we learned how to use Vuex Getters to override the local data fetching logic:

  • We need to startstoreAdd to instancegettersAttribute, and ingettersProperties to define different properties or methods.
  • In these different typesgetterIn, we can get local data.
  • We can call our property via property access and method accessgetter.

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