美文网首页Vue
使用Vuex实现购物车功能

使用Vuex实现购物车功能

作者: h2coder | 来源:发表于2023-08-20 12:10 被阅读0次

效果展示

image.png

依赖版本

  • vue2:2.6.14
  • vuex:3.6.2
  • axios:1.4.0
  • less-loader:11.1.3

准备数据

购物车列表

  • 服务端接口返回的购车列表数据
{
  "cart": [
    {
      "id": 100001,
      "name": "低帮城市休闲户外鞋天然牛皮COOLMAX纤维",
      "price": 128,
      "count": 2,
      "thumb": "https://yanxuan-item.nosdn.127.net/3a56a913e687dc2279473e325ea770a9.jpg"
    },
    {
      "id": 100002,
      "name": "网易味央黑猪猪肘330g*1袋",
      "price": 39,
      "count": 2,
      "thumb": "https://yanxuan-item.nosdn.127.net/d0a56474a8443cf6abd5afc539aa2476.jpg"
    },
    {
      "id": 100003,
      "name": "KENROLL男女简洁多彩一片式室外拖",
      "price": 128,
      "count": 2,
      "thumb": "https://yanxuan-item.nosdn.127.net/eb1556fcc59e2fd98d9b0bc201dd4409.jpg"
    },
    {
      "id": 100004,
      "name": "云音乐定制IN系列intar民谣木吉他",
      "price": 589,
      "count": 1,
      "thumb": "https://yanxuan-item.nosdn.127.net/4d825431a3587edb63cb165166f8fc76.jpg"
    }
  ]
}

页面分析

  • 页面标题栏
    • 固定显示“购物车”的文字
  • 购物车列表
    • 列表显示购物车数据,有商品图片、商品名称、商品价格、商品数量,以及数量的加、减按钮
  • 页面底部栏
    • 所有商品数量、商品总价、结算按钮

业务分析

  • 列表项
    • 点击加号,单项商品的数量 + 1
    • 点击减号,单项商品的数量 - 1,但数量不能少于1,少于1时,禁用按钮
  • 底部栏
    • 统计多少件商品,取数据数组的长度即可
    • 单项商品的价格 = 数量 * 单价
    • 统计商品总价,所有单项商品的价格累加和

Vuex配置

  • Vuex主要有6个配置部分,分别为
    • state,状态,也就是要共享的数据
    • mutations,同步修改state状态的函数
    • actions,异步修改state状态的函数,其实就是异步结束后,调用mutations的函数,实现修改state数据
    • getters,类似Vue组件的计算属性,可以对state状态数据进行统计、合并多个state为一个新的响应式变量
    • modules,模块,Vuex支持定义模块,每个模块都有自己的state、mutations、actions、getters

开始配置

  • src目录下,建立store目录,新建index.js
  • 建立modules目录,购物车使用分模块,单独建立一个cart模块,封装在cart.js
/* 封装Vuex相关 */
import Vue from 'vue'
import Vuex from 'vuex'

// 导入购物车模块的store
import cart from '@/store/modules/cart'

// 使用Vuex
Vue.use(Vuex)

// 创建vuex仓库实例
const vuex = new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    // 导入购物车模块的store
    cart
  }
})

// 默认导出
export default vuex

注册Vuex

import Vue from 'vue'
import App from './App.vue'
// 导入vuex实例
import store from './store'

Vue.config.productionTip = false

new Vue({
  // 注册vuex
  store,
  render: h => h(App)
}).$mount('#app')

购物车模块Vuex

  • state(状态)

    • list,购物车数据,将用于渲染购物车项
  • mutations(同步修改state的函数)

    • updateCartList,更新购物车数据,给list状态变量赋值
    • updateCartCount,更新指定id的购物车数量
  • actions(异步修改state的函数)

    • getCartAsync,调用接口,获取购物车列表数据,提交mutation,调用updateCartList函数
    • updateAsyncCartCount,调用接口,更新某个id的购物车项的数量,提交mutation,调用updateCartCount函数
  • getters(相当于计算属性)

    • totalPrice,总价格,统计state中的list中所有的商品的价格(商品价格 * 数量,再累加起来)
    • totalCount,总数量,统计state中的list中所有的商品的个数
  • 注意:必须添加namespaced属性,否则调用时,会找不到模块

/* 购物车模块的store */

// 引入axios
import axios from "axios"

// 状态响应式变量
const state = () => ({
    // 购物车数据列表
    list: []
})

// vuex的计算属性
const getters = {
    // 总价格
    totalPrice(state) {
        return state.list.reduce((prev, current) => {
            return prev + (current.count * current.price)
        }, 0)
    },
    // 总数量
    totalCount(state) {
        return state.list.reduce((prev, current) => {
            return prev + current.count
        }, 0)
    }
}

// 同步修改state变量
const mutations = {
    // 更新购物车数据
    updateCartList(state, list) {
        state.list = list
    },
    // 更新指定Id的购物车商品的数量
    updateCartCount(state, { id, count }) {
        const cartItem = state.list.find(item => item.id === id)
        cartItem.count = count
    }
}

// 异步修改state变量
const actions = {
    // 发送请求,获取购物车列表 
    async getCartAsync(context) {
        const result = await axios({
            url: 'http://localhost:3000/cart',
            method: 'GET'
        })
        console.log(result);

        // 提交mutation
        const list = result.data
        context.commit("updateCartList", list)
    },
    // 修改购物车的商品数量
    async updateAsyncCartCount(context, { id, count }) {
        // 发送请求,修改数量
        const result = await axios({
            url: `http://localhost:3000/cart/${id}`,
            method: 'PATCH',
            data: {
                count: count
            }
        })
        console.log(result);

        // 提交mutation,修改内存中购物车商品的数量
        context.commit("updateCartCount", { id, count })
    }
}

// 默认导出
export default {
    // 命名空间,设置为true,才能用使用vuex的辅助函数
    namespaced: true,
    state,
    getters,
    mutations,
    actions
}

编写组件

按页面分析,拆分为3个组件,分别为顶部栏组件、列表项组件、底部栏组件

顶部栏组件

<template>
  <div class="header-container">{{ title }}</div>
</template>

<script>
export default {
  name: "CartHeader",
  props: {
    // 标题文字
    title: {
      type: String,
      required: true
    }
  }
};
</script>

<style lang="less" scoped>
.header-container {
  height: 50px;
  line-height: 50px;
  font-size: 16px;
  background-color: #42b983;
  text-align: center;
  color: white;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 999;
}
</style>

底部栏组件

  • 组件需要传入2个参数,分别是:
    • 总价格
    • 总数量
<template>
  <div class="footer-container">
    <!-- 中间的合计 -->
    <div>
      <span>共 {{ totalCount }} 件商品,合计:</span>
      <span class="price">¥{{ totalPrice }}</span>
    </div>
    <!-- 右侧结算按钮 -->
    <button class="btn btn-success btn-settle">结算</button>
  </div>
</template>

<script>
export default {
  name: "CartFooter",
  props: {
    // 总价格
    totalPrice: {
      type: Number,
      required: true,
    },
    // 总数量
    totalCount: {
      type: Number,
      required: true,
    },
  },
};
</script>

<style lang="less" scoped>
.footer-container {
  background-color: white;
  height: 50px;
  border-top: 1px solid #f8f8f8;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 0 10px;
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  z-index: 999;
}

.price {
  color: red;
  font-size: 13px;
  font-weight: bold;
  margin-right: 10px;
}

.btn-settle {
  height: 30px;
  min-width: 80px;
  margin-right: 20px;
  border-radius: 20px;
  background: #42b983;
  border: none;
  color: white;
}
</style>

购物车项组件

  • 组件需要传入单个购物车项数据,组件中使用数据渲染,商品图片、商品名称、单价、数量等
  • 通过mapActions,将vuex中的actions函数,映射到methods
  • 定义updateCartCount函数,当点击+号和-号时,调用该函数,计算商品新的数量,再调用vuex中的updateAsyncCartCount函数,通知后端更新商品数量
  • 计算数量,定义isDisable函数,判断商品小于1时,为禁用状态
<template>
  <div class="goods-container">
    <!-- 左侧图片区域 -->
    <div class="left">
      <img :src="item.thumb" class="avatar" alt="" />
    </div>
    <!-- 右侧商品区域 -->
    <div class="right">
      <!-- 标题 -->
      <div class="title">{{ item.name }}</div>
      <div class="info">
        <!-- 单价 -->
        <span class="price">¥{{ item.price }}</span>
        <div class="btns">
          <!-- 按钮区域 -->
          <button
            :disabled="isDisable"
            @click="updateCartCount(item.id, -1)"
            class="btn btn-light"
          >
            -
          </button>
          <span class="count">{{ item.count }}</span>
          <button @click="updateCartCount(item.id, 1)" class="btn btn-light">
            +
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
// 引入vuex的辅助函数
import { mapActions } from "vuex";

export default {
  name: "CartItem",
  // 子组件参数声明
  props: {
    item: {
      type: Object,
      required: true,
    },
  },
  methods: {
    // 更新购物车的商品数量
    updateCartCount(id, unit) {
      // 计算新商品数量
      const count = this.item.count + unit;
      // 派发action,修改数量
      this.updateAsyncCartCount({
        id: id,
        count: count,
      });
    },
    // 引入异步action
    ...mapActions("cart", ["updateAsyncCartCount"]),
  },
  computed: {
    // 当购物车的商品数量小于等于1时,为禁用
    isDisable() {
      return this.item.count <= 1;
    },
  },
};
</script>

<style lang="less" scoped>
.goods-container {
  display: flex;
  padding: 10px;
  + .goods-container {
    border-top: 1px solid #f8f8f8;
  }
  .left {
    .avatar {
      width: 100px;
      height: 100px;
    }
    margin-right: 10px;
  }
  .right {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;
    .title {
      font-weight: bold;
    }
    .info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      .price {
        color: red;
        font-weight: bold;
      }
      .btns {
        .count {
          display: inline-block;
          width: 30px;
          text-align: center;
        }
      }
    }
  }
}

.custom-control-label::before,
.custom-control-label::after {
  top: 3.6rem;
}
</style>

根组件

  • 通过mapState映射函数,将购物车状态数据list,映射到computed计算属性中
  • 通过mapActions映射函数,将获取购物车数据函数getCartAsync,映射到methods
  • 通过mapGetters映射函数,将购物车totalPrice商品总价、totalCount商品总数量,映射到computed计算属性中
  • 通过v-for,循环渲染购物车项组件,并将单个购物车项数据传入给组件
  • 将购物车totalPrice商品总价、totalCount商品总数量,传给底部栏组件进行渲染
<template>
  <div class="app-container">
    <!-- Header 区域 -->
    <cart-header title="购物车"></cart-header>
    <!-- 商品 Item 项组件 -->
    <cart-item v-for="item in list" :key="item.id" :item="item"></cart-item>
    <!-- Foote 区域 -->
    <cart-footer
      :totalPrice="totalPrice"
      :totalCount="totalCount"
    ></cart-footer>
  </div>
</template>

<script>
// 标题栏
import CartHeader from "@/components/cart-header.vue";
// 购物车条目
import CartItem from "@/components/cart-item.vue";
// 底部栏
import CartFooter from "@/components/cart-footer.vue";

// 引入vuex的辅助函数
import { mapState, mapActions, mapGetters } from "vuex";

export default {
  name: "App",
  // 注册组件
  components: {
    CartHeader,
    CartFooter,
    CartItem,
  },
  created() {
    // 一进入页面,就派发action,获取购物车列表数据
    this.getCartAsync();
  },
  methods: {
    // 引入异步action
    ...mapActions("cart", ["getCartAsync"]),
  },
  computed: {
    // 引入状态响应式变量
    ...mapState("cart", ["list"]),
    // 引入vuex的计算属性
    ...mapGetters("cart", ["totalPrice", "totalCount"]),
  },
};
</script>

<style lang="less" scoped>
.app-container {
  padding: 50px 0;
  font-size: 14px;
}
</style>

相关文章

  • Vuex 状态管理

    Vue组件间通信方式 Vuex核心概念和基本使用 购物车案例 模拟实现vuex 一、组件内的状态管理流程 状态管理...

  • Vue实战(四) - 实战项目(下) - 路由初步/复杂状态管理

    0. 实战场景介绍 通过Vuex构建全局状态管理的购物车。通过路由功能实现购物车的跳转。 1. 创建空白购物车 s...

  • VUEX

    目标 能够说出VUEX的基本使用步骤 能够说出vuex的核心概念 能够基于vuex实现业务功能 目录 vuex概述...

  • vuex最简单使用

    vuex,即vue里面的一个插件,使用场景:需要一个购物车功能,可能有多个组件会调用到这个购物车功能,在首页添加了...

  • Vuex

    1.Vuex概述 2.Vuex基本使用 3.使用Vuex完成todo案例 1.Vuex概述 Vuex是实现组件全局...

  • 04 Vue中组件通信(下)

    使用vuex来实现组件通信一、vuex 的安装与使用 安装npm install vuex -S 使用 二、 vu...

  • Vuex(三) —— 纯手写一个超简单的Vuex

    目录 分析Vuex的功能 下载模板 分析模块结构 实现install函数 实现Store类 替换vuex 前面学了...

  • vuex状态管理工具的使用

    vuex使用介绍 使用步骤 下载vuex: npm i vuex -S 创建store实例, 实现数据管理 sto...

  • vuex常见面试题

    1.vuex是什么?怎么使用?哪种功能场景使用它? 2.vuex有哪几种属性 3.不使用Vuex会带来什么...

  • VUE·奇技淫巧——vuex会话储存

    vuex实现刷新不被清除功能 前几天开发遇到一个问题,就是使用vuex时候需要存储的东西在刷新情况下不被清理,因为...

网友评论

    本文标题:使用Vuex实现购物车功能

    本文链接:https://www.haomeiwen.com/subject/qspsmdtx.html