actions vuex 请求_Part3·Vue.js 框架源码与进阶 - Vuex 状态管理 - 购物车案例

8.5 Vuex 购物车案例

接下来我们通过一个购物车案例来演示 Vuex 在项目中的使用方式,首先把购物车的 项目模板 下载下来。

案例演示

  • 完整案例代码仓库

运行项目

# 第一个进程
node server

# 第二个进程
npm run serve

运行效果

b30eb32ea12d66f6377f5fa707371914.png

39ad021f2bace0b3208c69513fb8da24.png

fc51d8abe86d0320e300824180bb7a4d.png

12298d0d097718f5898bc82457498fff.png

功能列表

  • 商品列表组件
    • 展示商品列表
    • 添加购物车
  • 商品列表中弹出框组件
  • 购物车列表组件

商品列表

商品列表功能

  • Vuex 中创建两个模块,分别用来记录商品列表和购物车的状态,store 的结构:

8ddfcc7497d96cb288abc73fb0c7d611.png
  • products 模块,store/modules/products.js
import axios from 'axios'

const state = {
  products: [] // 记录所有商品
}
const getters = {}
const mutations = {
  // 修改商品数据状态
  setProducts (state, payload) {
    state.products = payload
  }
}
// 请求接口获取数据
const actions = {
  async getProducts ({ commit }) {
    const { data } = await axios({
      method: 'GET',
      url: 'http://127.0.0.1:3000/products'
    })
    // 提交 Mutation
    commit('setProducts', data)
  }
}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}
  • store/index.js 中注册 products.js 模块
import products from './modules/products'
  • views/products.vue 中实现商品列表的功能
import { mapState, mapActions } from 'vuex'
export default {
  name: 'ProductList',
  computed: {
    ...mapState('products', ['products'])
  },
  methods: {
    ...mapActions('products', ['getProducts'])
  },
  created () {
    this.getProducts()
  }
}

添加购物车

  • cart 模块实现添加购物车功能,store/modules/cart.js
const mutations = {
  addToCart (state, product) {
    // 1. cartProducts 中没有该商品,把该商品(product)添加到数组,
    // 并增加 count=1,isChecked=true,totalPrice
    // 2. cartProducts 有该商品,让商品的数量(count)加1,选中,计算小计
    const prod = state.cartProducts.find(item => item.id === product.id)
    if (prod) {
      prod.count++
      prod.isChecked = true
      prod.totalPrice = prod.count * prod.price
    } else {
      state.cartProducts.push({
        ...product,
        count: 1,
        isChecked: true,
        totalPrice: product.price
      })
    }
  }
}
  • store/index.js 中注册 cart 模块
import cart from './modules/cart'
  • view/products.vue 中实现添加购物车功能
<!-- 修改模板 -->
<!-- <template slot-scope="scope"> -->
<template v-slot="scope">
  <el-button @click="addToCart(scope.row)">加入购物车</el-button>
</template>
<!-- 映射 cart 中的 mutations -->
import { mapMutations } from 'vuex'

methods: {
  ...mapMutations('cart', ['addToCart'])
}

我的购物车 - 列表

  • components/pop-cart.vue 中展示购物车列表
import { mapState } from 'vuex'

export default {
  name: 'PopCart',
  computed: {
    ...mapState('cart', ['cartProducts'])
  }
}

我的购物车 - 统计

  • cart 模块实现统计总数和总价,store/modules/cart.js
const state = {
  cartproducts: []
}

const getters = {
  totalCount (state) {
    return state.cartProducts.reduce((count, prod) => {
      return count + prod.count
    }, 0)
  },
  totalPrice (state) {
    return state.cartProducts.reduce((count, prod) => {
      return count + prod.totalPrice
    }, 0).toFixed(2)
  }
}
  • components/pop-cart.vue 中显示徽章和小计
<div>
  <p>共 {{ totalCount }} 件商品 共计¥{{ totalPrice }}</p>
  <el-button size="mini" type="danger" @click="$router.push({ name: 'cart' })">
去购物车</el-button>
</div>
<el-badge :value="totalCount" class="item" slot="reference">
  <el-button type="primary">我的购物车</el-button>
</el-badge>
import { mapGetters } from 'vuex'

computed: {
  ...mapGetters('cart', ['totalCount', 'totalPrice'])
}

我的购物车 - 删除

  • cart 模块实现从购物车删除的功能,store/modules/cart.js
// mutations 中添加

deleteFromCart (state, prodId) {
  const index = state.cartProducts.findIndex(item => item.id === prodId)
  index !== -1 && state.cartProducts.splice(index, 1)
}
  • components/pop-cart.vue 中实现删除功能
<template slot-scope="scope">
  <el-button
    @click="deleteFromCart(scope.row.id)"
    size="mini"
  >删除</el-button>
</template>
import { mapMutations } from 'vuex'

methods: {
  ...mapMutations('cart', ['deleteFromCart'])
}

购物车组件 - 购物车列表

  • views/cart.vue 中展示购物车列表
import { mapState } from 'vuex'

computed: {
  ...mapState('cart', ['cartProducts']),
}

购物车组件 - 全选功能

  • cart 模块实现更新商品的选中状态,store/modules/cart.js
// 更新所有商品的选中状态(点击全选)
updateAllProductChecked (state, checked) {
  state.cartProducts.forEach(prod => {
    prod.isChecked = checked
  })
},
// 更新某个商品的选中状态(点击单个商品)
updateProductChecked (state, {
  checked,
  prodId
}) {
  const prod = state.cartProducts.find(prod => prod.id === prodId)
  prod && (prod.isChecked = checked)
}
  • views/cart.vue,实现全选功能
    • 使用事件抛出一个值
<el-table-column width="55">
  <template v-slot:header>
    <el-checkbox v-model="checkedAll" size="mini">
    </el-checkbox>
  </template>
  <!--
    @change="updateProductChecked"  默认参数:更新后的值
    @change="updateProductChecked(productId, $event)"  123, 原来那个默认参数
    当你传递了自定义参数的时候,还想得到原来那个默认参数,就手动传递一个 $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>
import { mapMutations } from 'vuex'

computed: {
  checkedAll: {
    get () {
      return this.cartProducts.every(prod => prod.isChecked)
    },
    set (value) {
      this.updateAllProductChecked(value)
    }
  }
},
methods: {
  ...mapMutations('cart', [
    'updateAllProductChecked',
    'updateProductChecked'
  ])
}

购物车组件 - 数字文本框

  • cart 模块实现更新商品数量,store/modules/cart.js
updateProduct (state, {
  prodId,
  count
}) {
  const prod = state.cartProducts.find(prod => prod.id === prodId)
  if (prod) {
    prod.count = count
    prod.totalPrice = count * prod.price
  }
}
  • views/cart.vue,实现数字文本框功能
<el-table-column
  prop="count"
  label="数量">
  <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>
methods: {
  ...mapMutations('cart', [
    'updateProduct'
  ])
}

购物车组件 - 统计

  • cart 模块实现统计选中商品价格和数量,store/modules/cart.js
checkedCount (state) {
  return state.cartProducts.reduce((sum, prod) => {
    if (prod.isChecked) {
      sum += prod.count
    }
    return sum
  }, 0)
},
checkedPrice (state) {
  return state.cartProducts.reduce((sum, prod) => {
    if (prod.isChecked) {
      sum += prod.totalPrice
    }
    return sum
  }, 0)
}
  • views/cart.vue,实现小计
<p>已选 <span>{{ checkedCount }}</span> 件商品,总价:<span>{{ 
checkedPrice }}</span></p>
import { mapGetters } from 'vuex'

computed: {
  ...mapGetters('cart', ['checkedCount', 'checkedPrice']),
}

购物车组件 - 删除

  • views/cart.vue,实现删除功能
<template slot-scope="scope">
  <el-button @click="deleteFromCart(scope.row.id)" size="mini">删除</el-button>
</template>
methods: {
  ...mapMutations('cart', [
    'deleteFromCart'
  ])
}

本地存储

  • store/modules/cart.js,获取存储在本地的数据
const state = {
  // 把字符串转换为对象
  cartProducts: JSON.parse(window.localStorage.getItem('cart-products')) || []
}
  • store/index.js,当购物车的数据更改,重新存储到localStorage
    • Vuex 插件介绍
      • Vuex 的插件就是一个函数
      • 这个函数接收一个 store 的参数

因此我们可以注册一个函数让它可以在所有的mutation结束之后执行:

const myPlugin = store => {
  // 当 store 初始化后调用
  store.subscribe((mutation, state) => {
    // 每次 mutation 之后调用
    // mutation 的格式为 { type, paylod }
    if (mutation.type.startsWith('cart/')) { // 判断是否为cart模块的mutation
      window.localStorage.setItem('cart-products', JSON.stringify(state.cart.cartProducts))
    }
  })
}

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    products,
    cart
  },
  // 注册插件
  plugins: [myPlugin]
})Part3·Vue.js 框架源码与进阶 - Vuex 状态管理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值