[番外] 一步一步实现购物车功能 [续]

状态管理

建立一个空间来储存应用程序的 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

每日一句:
收假恐慌症


<<:  Day26 - 使用 Guard 来实作一个马克杯的状态机

>>:  Day29 - 上线後疑难杂症纪录

我选择的学习语言跟框架

我选了python当作主要开发语言 因为我以前有用过python而且很潮 框架部分我选比较主流的Dj...

副业对我来说是什麽

我们的app 对我来说是什麽 是「自由」吗? 因为有这个误打误撞的产品 我才可以离开微软工作 但同时...

滑鼠键盘的无线世界 - Uifying /蓝芽

朋友送了一组键盘滑鼠.Logitech 键盘yr0009 & 滑鼠M215 想要滑鼠放家里用...

Day 12 CSS <圆角边框、盒子阴影>

圆角边框 使用border-radius圆角边框样式,可以修改盒子边框变成圆角 语法: border...

GCP 自动开关机

最近将案子上了 GCP 因为是小成本案子,如果一直让机器开机在上面跑,累积下来的金额应该很可怕 所以...