iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Modern Web

別再說我不會框架,網頁 Vue 起來!系列 第 26

[番外] 一步一步實現購物車功能 [續]

狀態管理

建立一個空間來儲存應用程式的 store state

  • store 資料夾 放在 src 下
  • store 中包含 product 和 cart 資料夾
src/
 |
 |---store/
      |
      |--- modules/
             |
             |--- product
             |--- cart

分別在 product 和 cart 資料夾新增 index.js

// vuex-shopping-cart/src/store/modules/product/index.js
import axios from 'axios';
const state = {
  productItems: []
}
// vuex-shopping-cart/src/store/modules/cart/index.js
import axios from 'axios';
const state = {
  cartItems: []
}

接著建立 mutations,在 state 後方增加一個 section

// vuex-shopping-cart/src/store/modules/product/index.js
// 略
const mutations = {
  UPDATE_PRODUCT_ITEMS (state, payload) {
    state.productItems = payload;
  }
}

建立一個 mutation 物件,包含一個 UPDATE_PRODUCT_ITEMS 方法,用來設定 productItems 值。

cart/index.js 也比照處理

// 略
const mutations = {
  UPDATE_CART_ITEMS (state, payload) {
    state.cartItems = payload;
  }
}

接著建立actions,用來處理 mutations

// vuex-shopping-cart/src/store/modules/product/index.js
// 略
const actions = {
  getProductItems ({ commit }) {
    axios.get(`/api/products`).then((response) => {
      commit('UPDATE_PRODUCT_ITEMS', response.data)
    });
  }
}
// getProductItems 會利用 axios 發送一個非同步的 GET 請求,請求成功時,呼叫 UPDATE_PRODUCT_ITEMS mutation (傳入 response 資料) 

cart/index.js 同樣也比照處理

// vuex-shopping-cart/src/store/modules/cart/index.js
const actions = {
  getCartItems ({ commit }) {
    axios.get('/api/cart').then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  },
  addCartItem ({ commit }, cartItem) {
    axios.post('/api/cart', cartItem).then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  },
  removeCartItem ({ commit }, cartItem) {
    axios.delete('/api/cart/delete', cartItem).then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  },
  removeAllCartItems ({ commit }) {
    axios.delete('/api/cart/delete/all').then((response) => {
      commit('UPDATE_CART_ITEMS', response.data)
    });
  }
}

再來使用 getters 來取得所有 product 的資訊

// vuex-shopping-cart/src/store/modules/product/index.js
const getters = {
  productItems: state => state.productItems,
  productItemById: (state) => (id) => {
    return state.productItems.find(productItem => productItem.id === id)
  }
}

將 product 和 cart modules 中的 state mutations actions getters 匯出,讓應用程式中其他元件也可以取得。

// vuex-shopping-cart/src/store/modules/product/index.js
// 略
const productModule = {
  state,
  mutations,
  actions,
  getters
}

export default productModule;
// vuex-shopping-cart/src/store/modules/product/index.js
// 略
const cartModule = {
  state,
  mutations,
  actions,
  getters
}
export default cartModule;

最後整合到 Vuex 中,建立 src/store/index.js

// vuex-shopping-cart/src/store/index.js
import { createStore } from 'vuex'
import product from'./modules/product';
import cart from './modules/cart';

export default createStore({
  modules: {
    product,
    cart
  }
})

UI 介面設定

編輯 vuex-shopping-cart/src/router/index.js 設定路由

import { createRouter, createWebHashHistory } from 'vue-router'
import CartList from '../components/cart/Cart_List.vue';
import ProductList from '../components/product/Product_List.vue';

const routes = [
  {
    path: '/inventory',
    component: ProductList
  },
  {
    path: '/cart',
    component: CartList
  },
  {
    path: '/',
    redirect: '/inventory'
  },
]
const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

三個路由路徑 /inventory /cart /

在src/components 下新增三個資料夾以及會用到的元件

  • core 基本元件,ex: 導覽列
    • Navbar.vue
  • cart 購物車中品項,列表的畫面
    • Cart_List_Item.vue
    • Cart_List.vue
  • product 產品品項或產品列表的畫面
    • Product_List_Item.vue
    • Product_List.vue

接下來編輯個元件的內容,例如

// vuex-shopping-cart/src/components/core/Navbar.vue
<template>
    <nav class="navbar" role="navigation" aria-label="main navigation">
      <div class="navbar-brand">
        <a
          role="button"
          class="navbar-burger burger"
          aria-label="menu"
          aria-expanded="false"
          data-target="navbarBasicExample"
        >
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
          <span aria-hidden="true"></span>
        </a>
      </div>
      <div id="navbarBasicExample" class="navbar-menu">
        <div class="navbar-end">
          <div class="navbar-item">
            <div class="buttons">
              <router-link to="/inventory" class="button is-primary">
               <strong> Inventory</strong>
              </router-link>
              <router-link to="/cart"  class="button is-warning">   <p>
    Total cart items:
    <span> {{cartQuantity}}</span> </p>
              </router-link>
            </div>
          </div>
        </div>
      </div>
    </nav>
</template>
<script>
import {mapGetters} from "vuex"
export default {
    name: "Navbar",
    computed: {
    ...mapGetters([
      'cartQuantity'
    ])
  },
  created() {
    this.$store.dispatch("getCartItems");
  }
}
</script>

ex: vuex-shopping-cart/src/components/product/Product_List.vue

<template>
  <div class="container is-fluid">
    <div class="tile is-ancestor">
      <div class="tile is-parent" v-for="productItem in productItems" :key="productItem.id">
      <ProductListItem :productItem="productItem"/>
      </div>
    </div>
  </div>
</template>
<script>
import { mapGetters } from 'vuex';
import Product_List_Item from './Product_List_Item'
export default {
  name: "ProductList",
  components: {
    ProductListItem:Product_List_Item
  },
  computed: {
    ...mapGetters([
      'productItems'
    ])
  },
  created() {
    this.$store.dispatch('getProductItems');
  }
};
</script>

其他各個元件的內容可以參考 How To Build a Shopping Cart with Vue 3 and Vuex


下篇預告

  • 未定.../images/emoticon/emoticon04.gif

每日一句:
收假恐慌症


上一篇
[番外] 一步一步實現購物車功能 [序]
下一篇
路由把關者- Navigation Guards
系列文
別再說我不會框架,網頁 Vue 起來!30

尚未有邦友留言

立即登入留言