directory

  • demand
  • Demand analysis
    • Component analysis
    • Component communication
  • The development of
    • Prepare the environment
    • Preparing the module structure
    • Item List component
      • Show a list of goods
      • Add shopping cart
    • My shopping cart component
      • Shopping cart list
      • Commodity quantity and statistical function
      • Delete cart items
    • Shopping cart list component
      • Shopping cart list
      • selection
      • Add and subtract figures and make subtotals
      • Delete function
      • Count the total amount and the total money
      • Deal with decimals
    • The local store
  • Complete the case

The previous section introduced the core principles and simple use of Vuex. Here is a practical example

demand

  • The item list shows the item, price and add to cart button
    • Click “Add to Shopping cart” button to add to shopping cart, “My Shopping cart” prompt quantity increases
  • My shopping cart button
    • Mouse hover appearspopover, display the items in the cart, price quantity, delete button, total quantity and total price, and go to cart button
    • [Delete] Button can delete the whole product, the total price and quantity will change
    • Click “Go to shopping cart” button to jump to the shopping cart interface
  • Display multiple boxes, commodity, unit price, quantity and [add/subtract button car], subtotal, [delete] button, total and total price, [settlement] button
    • Quantity plus or minus changes quantity, subtotal, total quantity and total price
    • [Delete] Button deletes the entire product
    • Multi-check box is not selected in total quantity and total price.
  • Refresh the page, the state is still there, stored in local storage

Demand analysis

Component analysis

  • Routing component
    • List of Goods (①)
    • Shopping cart List (②)
  • My shopping cart Pop-up component (③)

Component communication

② and ③ both depend on the data of the shopping cart. Click add shopping cart in ① to transfer the data to ② and ③. The data modification between ② and ③ also depends on each other.

The development of

Prepare the environment

  1. Download the templatevuex-cart-demo-template, the routing components, style components and data have been written, we only need to be responsible for the implementation of the function. There’s another one in the projectserver.jsFile, this isnodeUsed to simulate the interface.
const _products = [
  { id: 1.title: 'iPad Pro'.price: 500.01 },
  { id: 2.title: 'H&M T-Shirt White'.price: 10.99 },
  { id: 3.title: 'Charli XCX - Sucker CD'.price: 19.99 }
]

app.use(express.json())
// Simulate commodity data
app.get('/products'.(req, res) = > {
  res.status(200).json(_products)
})
// Simulate payment
app.post('/checkout'.(req, res) = > {
  res.status(200).json({
    success: Math.random() > 0.5})})Copy the code
  1. First of all,npm installInstall dependencies afternode serverRun the interface, and then add terminal inputnpm run serveGet the project running, this time accesshttp://127.0.0.1:3000/productsAccess to data, accesshttp://localhost:8080/You can access the page

Preparing the module structure

  1. instoreCreate in foldermodulesFolder to create two modulesproducts.jsandcart.js

  1. inproducts.jsandcart.jsSet up the basic structure in the file
const state = {}
const getters = {}
const mutations = {}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}
Copy the code
  1. inindex.jsImport and reference modules in
import Vue from 'vue'
import Vuex from 'vuex'
// 1. Import modules
import products from './modules/products'
import cart from './modules/cart'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {},mutations: {},actions: {},// 2. Reference modules
  modules: {
    products,
    cart
  }
})
Copy the code

Item List component

  • Show a list of goods
  • Add shopping cart

Show a list of goods

  1. inproducts.jsTo implement the following methods
  • instateDefines a property to record all commodity data
  • inmutationsTo add methods to modify commodity data
  • inactionsAdd a method to request data asynchronously from the interface
/ / import axios
import axios from 'axios'
const state = {
  // Record all items
  products: []}const getters = {}
const mutations = {
  // Assign to products
  setProducts (state, payLoad) {
    state.products = payLoad
  }

}
const actions = {
  // Asynchronously obtain goods, the first is the context, destruct to commit
  async getProducts ({ commit }) {
    // Request the interface
    const { data } = await axios({
      method: 'GET'.url: 'http://127.0.0.1:3000/products'
    })
    // Store the result of the obtained data in state
    commit('setProducts', data)
  }
}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

Copy the code
  1. inproducts.vueThe originaldataDelete, import, and use the module
<script>
// Import the required modules
import { mapActions, mapState } from 'vuex'
export default {
  name: 'ProductList'.// Create a calculation property mapping products data, because the namespace is enabled, add the namespace notation, after the mapping property products
  computed: {
    ...mapState('products'['products'])},// Map the methods in actions. The first one is still the namespace
  methods: {
    ...mapActions('products'['getProducts'])},// After the component is created, call getProducts to get data
  created () {
    this.getProducts()
  }
}
</script>
Copy the code
  1. When you open your browser, you can see that three items already appear in the product interface.

Add shopping cart

Store the currently clicked item in a location that can be accessed later in the shopping cart list component, so you need a location to record all the shopping cart data that can be shared among multiple components, so put this data in the Cart module

  1. In the modulecart.jsWrite the data in the
const state = {
  // Record the shopping cart item data
  cartProducts: []}const getters = {}
const mutations = {
  // payLoad is the payLoad
  addToCart (state, product) {
    // 1. Add the item to the array if there is no item and add count, isChecked, totalPrice
    // 2. If the item is available, add 1 to the item and select it to calculate the subtotal
    // Check if there is an item, return the item
    const prod = state.cartProducts.find(item= > item.id === product.id)

    if (prod) {
      // Quantity of this item +1
      prod.count++
      / / selected
      prod.isChecked = true
      // Subtotal = quantity * unit price
      prod.totalPrice = prod.count * prod.price
    } else {
      // Add a new item to the list of items
      state.cartProducts.push({
        // The content of the original products. product,/ / the number of
        count: 1./ / selected
        isChecked: true.// Subtotal the current unit price
        totalPrice: product.price
      })
    }
  }
}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}
Copy the code
  1. inproducts.vueIn the importcartAdd shopping cartmutation
<template>
  <div>.<el-table
      :data="products"
      style="width: 100%">.<el-table-column
        prop="address"
        label="Operation">
        <! -- This line can get scoped data through slot -->
        <! -- <template slot-scope="scope">
        <template v-slot="scope">
          <! Add click event, pass current list -->
          <el-button @click="addToCart(scope.row)">Add to shopping cart</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import { mapActions, mapMutations, mapState } from 'vuex'
export default {
  name: 'ProductList'.computed: {
    ...mapState('products'['products'])},methods: {
    ...mapActions('products'['getProducts']),
    // Map the data for adding shopping items to methods. mapMutations('cart'['addToCart'])
  },
  created () {
    this.getProducts()
  }
}
</script>

<style></style>

Copy the code
  1. Click on the browser, you can click on the Add to cart button, click on the debug console to see the data changes

My shopping cart component

  • List of goods to buy
  • Count the total number of shopping carts and the total price
  • The delete button

Shopping cart list

  1. incomponent/pop-cart.vueTo import shopping cart data
<template>
  <el-popover
    width="350"
    trigger="hover"
  >
  <! -- Here is the data of cartProducts, which need not be modified -->
    <el-table :data="cartProducts" size="mini">
      <el-table-column property="title" width="130" label="Goods"></el-table-column>.</el-table>.</el-popover>
</template>

<script>
// Import vuEX module
import { mapState } from 'vuex'
export default {
  name: 'PopCart'.computed: {
    // Import cartProducts from cart module. mapState('cart'['cartProducts'])}}</script>

<style></style>

Copy the code
  1. Open the browser, click add Shopping cart, you can see the pop-up window of the new added goods

Commodity quantity and statistical function

  1. Because totals and aggregates workstoreIn thegettersBecause it’s a simple modification of the data incart.jsthegettersIt reads:
const getters = {
  // Accept state as an argument and return the result
  totalCount (state) {
    // Return the sum of an element in the array using reduce
    The reduce method takes two arguments. The first argument is a function and the second argument is the start number (here starting from 0).
    // The function takes two arguments internally. The first argument is the summation variable, and the second argument is the element of the array
    return state.cartProducts.reduce((sum, prod) = > sum + prod.count, 0)},// Write the same as above
  totalPrice () {
    return state.cartProducts.reduce((sum, prod) = > sum + prod.totalPrice, 0)}}Copy the code
  1. incomponents/pop-cart.vueReferenced in the
<template>
  <el-popover
    width="350"
    trigger="hover"
  >.<div>
      <! Interpolate total and total
      <p>{{totalPrice}} ¥{{totalPrice}}</p>
      <el-button size="mini" type="danger" @click="$router.push({ name: 'cart' })">Go to the shopping cart</el-button>
    </div>
    <! Here, change value to totalCount -->
    <el-badge :value="totalCount" class="item" slot="reference">
      <el-button type="primary">My shopping cart</el-button>
    </el-badge>
  </el-popover>
</template>

<script>
// Import mapGetters
import { mapGetters, mapState } from 'vuex'
export default {
  name: 'PopCart'.computed: {
    ...mapState('cart'['cartProducts']),
    // Import totalCount and totalPrice from the cart module. mapGetters('cart'['totalCount'.'totalPrice'])}}</script>

<style>

</style>

Copy the code
  1. Open your browser, add two items, and you can see that the badges and totals have changed

Delete cart items

Deleting an item changes the state in the CART module, so add a mutation to the CART module

  1. incardthemutationadd
const mutations = {
  addToCart (state, product) {
    ...
  },
  // Delete the shopping cart item. The second parameter is the item ID
  deleteFromCart (state, prodId) {
    // Use the array findIndex to get the index
    const index = state.cartProducts.findIndex(item= > item.id === prodId)
    // Check if this is equal to -1, if not, perform the following delete element
    // splice receives the index of the deleted element. The second element is the deleted element. Write 1 hereindex ! = = -1 && state.cartProducts.splice(index, 1)}}Copy the code
  1. incomponents/pop-cart.vueReferenced in the
<template>
  <el-popover
    width="350"
    trigger="hover"
  >
    <el-table :data="cartProducts" size="mini">.<el-table-column label="Operation">
        <! Get the id of the current element, add slot slot -->
        <template v-slot="scope">
          <el-button size="mini" @click="deleteFromCart(scope.row.id)">delete</el-button>
        </template>
      </el-table-column>
    </el-table>.</el-popover>
</template>

<script>
// Import the mapMutations module
import { mapGetters, mapMutations, mapState } from 'vuex'
export default {
  name: 'PopCart'.computed: {... },methods: {
    // Map deleteFromCart from cart to methods. mapMutations('cart'['deleteFromCart'])}}</script>

<style></style>

Copy the code
  1. Preview the product in the browser and click the delete button after adding the product

Shopping cart list component

  • Shopping cart list
  • selection
  • Add and subtract figures and make subtotals
  • Delete function
  • Collect statistics of selected commodity prices and quantities

Shopping cart list

  1. Introduce vuex in views/cart.vue
<template>
  <div>.<! CartProducts -->
    <el-table
      :data="cartProducts"
      style="width: 100%"
    >.</el-table>.</div>
</template>

<script>
/ / import vuex
import { mapState } from 'vuex'
export default {
  name: 'Cart'.computed: {
    // Map cartProducts to computed. mapState('cart'['cartProducts'])}}</script>

<style></style>

Copy the code
  1. In the browser, I add the item to my shopping cart, and I have the corresponding data in the shopping cart list

selection

  • Click on the subcheckbox, selected to unselected, unselected to selected
    • The childcheckboxThe state of is its commodityisCheckedThe value of the decision
    • usemutation
  • Click on the parentcheckboxWhen, soncheckboxConsistent with the parent, and the computed value is recalculated. Total point neutroncheckboxFather,checkboxWill be selected
    • The fathercheckboxIs displayed separately on the shopping cart page and does not need to be written tostoreTo write directly to the current component.
    • Its depend on the childcheckboxtheisCheckedState, so use computed properties
    • Change the parentcheckboxThe status of thestoreThe child state of thesetMethods can be
  1. So let’s write the changecheckboxThe state of themutation
const mutations = {
  addToCart (state, product) {
    ...
  },
  deleteFromCart (state, prodId) {
    ...
  },
  // Change the isChecked properties of all items
  // Take two arguments, the second is the checkbox status
  updateAllProductChecked (state, checked) {
    // Give each item the isChecked attribute to the checkbox state
    state.cartProducts.forEach(prod= > {
      prod.isChecked = checked
    })
  },
  // Change the isChecked property of an item
  // We need two properties, the second is the item object, which is destructed, one is checked, and one is id
  updateProductChecked (state, {
    checked,
    prodId
  }) {
    // Find the commodity object with the corresponding ID
    const prod = state.cartProducts.find(item= > item.id === prodId)
    // Assign isChecked to the goods object if it exists
    prod && (prod.isChecked = checked)
  }
}
Copy the code
  1. inviews/cart.vueTo import changes
  • The introduction ofmutation
  • Find the fathercheckboxBinding computed properties
  • definecheckboxCalculate the properties, donegetandset
  • The childcheckboxThe use of
<template>
  <div>.<el-table
      :data="cartProducts"
      style="width: 100%"
    >
      <el-table-column
        width="55">
        <template v-slot:header>
          <! -- 2. Here bind a v-model, calculate the property -->
          <el-checkbox size="mini" v-model="checkedAll">
          </el-checkbox>
        </template>
         <! 4.1 Bind the isChecked property of vuex first. 4.2 Register the change event "change" when the checkbox is changed. Select id from scope.row and checked state from $event -->
        <template v-slot="scope">
          <el-checkbox
            size="mini"
            :value="scope.row.isChecked"
            @change="updateProductChecked({ prodId: scope.row.id, checked: $event })"
          >
          </el-checkbox>
        </template>
      </el-table-column>.</el-table>.</div>
</template>

<script>
import { mapMutations, mapState } from 'vuex'
export default {
  name: 'Cart'.computed: {
    ...mapState('cart'['cartProducts']),
    // check the status of the parent checkbox
    checkedAll: {
      // Returns whether all items in the current cart are selected, or false if none is selected
      get () {
        return this.cartProducts.every(prod= > prod.isChecked)
      },
      // This method is triggered when the state changes and requires a single parameter, the checkbox state
      set (value) {
        this.updateAllProductChecked(value)
      }
    }
  },
  methods: {
    Map mutations of the CART module to methods. mapMutations('cart'['updateAllProductChecked'.'updateProductChecked'])}}</script>

<style></style>

Copy the code
  1. Open the browser, select the goods into the shopping cart, you can click on the full selection box

Add and subtract figures and make subtotals

  1. incartIn the module, define amutationMethod, update goods
const mutations = {
  ...
  // Update the item, deconstruct the item id and count
  updateProduct (state, { prodId, count }) {
    // Find the current item
    const prod = state.cartProducts.find(prod= > prod.id === prodId)
    // Update quantity and total price if found
    if (prod) {
      prod.count = count
      prod.totalPrice = count * prod.price
    }
  }
}
Copy the code
  1. Go to thecart.vueAdd amapMutations
<script>.export default{...methods: {
    // Map mutations for the CART module to methods. mapMutations('cart'['updateAllProductChecked'.'updateProductChecked'.'updateProduct'])}}</script>
Copy the code
  1. Method binding in the number box
<el-table-column
    prop="count"
    label="The number">
    <! $event = 'updateProduct'; $event = 'updateProduct'; $event = 'event';
    <template v-slot="scope">
      <el-input-number :value="scope.row.count" @change="updateProduct({ prodId: scope.row.id, count: $event })" size="mini"></el-input-number>
    </template>
  </el-table-column>
Copy the code
  1. View in the browser, add goods, modify the number, there will be the corresponding number of goods and subtotals

Delete function

  1. Had been in thecart.jsThere are delete goods in the module ofmutationHere is directly used incart.vueadd
<script>.export default{...methods: {
    // Map mutations for the CART module to methods. mapMutations('cart'['updateAllProductChecked'.'updateProductChecked'.'updateProduct'.'deleteFromCart'])}}</script>
Copy the code
  1. Define methods in the delete button above
<el-table-column
    label="Operation">
    <! -- Define a slot, delete the button binding event, pass in the item ID -->
    <template v-slot="scope">
    <el-button size="mini"
        @click="deleteFromCart(scope.row.id)">delete</el-button>
    </template>
</el-table-column>
Copy the code
  1. In the browser, after adding the commodity, enter the shopping cart page and click the delete button to delete the entire commodity.

Count the total amount and the total money

Conditions must be added to determine whether the current item is selected.

  1. incart.jsthegettersAdd the quantity of goods and the total price of the method, and select the state of judgment
const getters = {
  totalCount (state) {
    ...
  },
  totalPrice () {
    ...
  },
  // The number of selected items
  checkedCount (state) {
    // Check if it is selected before returning, if so, add it, and return sum
    return state.cartProducts.reduce((sum, prod) = > {
      if (prod.isChecked) {
        sum += prod.count
      }
      return sum
    }, 0)},// The price of the selected item is the same as above
  checkedPrice () {
    return state.cartProducts.reduce((sum, prod) = > {
      if (prod.isChecked) {
        sum += prod.totalPrice
      }
      return sum
    }, 0)}}Copy the code
  1. incart.vueIn the importmapGetters
<script>
import { mapGetters, mapMutations, mapState } from 'vuex'
export default {
  name: 'Cart'.computed: {
    ...mapState('cart'['cartProducts']),
    // Map getters in the CART module to computed. mapGetters('cart'['checkedCount'.'checkedPrice']),... },... }</script>
Copy the code
  1. Quote at the total price
<div>
  <p>The selected<span>{{ checkedCount }}</span>Item, total price:<span>{{ checkedPrice }}</span></p>
  <el-button type="danger">settlement</el-button>
</div>
Copy the code

Deal with decimals

When adding more goods, it is found that the amount of goods will appear many decimal places, so it is processed here

  1. mutationsThe product of prices is reserved for two decimal places
const mutations = {
  // Add goods
  addToCart (state, product) {
    const prod = state.cartProducts.find(item= > item.id === product.id)
    if (prod) {
      prod.count++
      prod.isChecked = true
      // Subtotal = quantity * unit price
      prod.totalPrice = (prod.count * prod.price).toFixed(2)
      console.log(prod.totalPrice)
    } else{... }},// Update the product
  updateProduct (state, { prodId, count }) {
    const prod = state.cartProducts.find(prod= > prod.id === prodId)
    if (prod) {
      prod.count = count
      // Keep two decimal places
      prod.totalPrice = (count * prod.price).toFixed(2)}}}Copy the code
  1. ingettersPut the total price in two decimal places and remember to convert it to a number
const getters = {
  // Total price
  totalPrice () {
    return state.cartProducts.reduce((sum, prod) = > sum + Number(prod.totalPrice), 0).toFixed(2)},// The price of the selected item
  checkedPrice () {
    return state.cartProducts.reduce((sum, prod) = > {
      if (prod.isChecked) {
        sum += Number(prod.totalPrice)
      }
      return sum
    }, 0).toFixed(2)}}Copy the code

The local store

When we refresh the page, the shopping cart data disappears because we add the data to memory storage, and when we actually shop, there are two ways to store it:

  • If the user logs in, the shopping cart data is in the server
  • If the user is not logged in, the shopping cart data is stored locally

Now implement the local storage function

  1. First of all incart.jsThe first time you enter the interface, the data is retrieved locally
const state = {
  // Get cart item data locally, if not initialized to an empty array
  cartProducts: JSON.parse(window.localStorage.getItem('cart-products') | | []}Copy the code
  1. inmutationsTo change the data, so every time the changed data, need to be recorded to the local storage, used herevuexPlugins inindex.jsIn the
. Vue.use(Vuex)const myPlugin = store= > {
  store.subscribe((mutation, state) = > {
    The format of // mutation is {type, payload}
    Cart /cart products /cart products
    // State is {cart, products}
    if (mutation.type.startsWith('cart/')) {
      // Local storage cartProducts
      window.localStorage.setItem('cart-products'.JSON.stringify(state.cart.cartProducts))
    }
  })
}
export default new Vuex.Store({
  ...
  // Mount myPlugin to Store
  plugins: [myPlugin]
})

Copy the code
  1. Refresh the browser to see that the shopping cart item list data still exists.

Complete the case

vuex-cart-temp