7 综合案例 - 创建项目
- 脚手架新建项目 (注意:勾选vuex)
版本说明:
vue2 vue-router3 vuex3
vue3 vue-router4 vuex4/pinia
vue create vue-cart-demo
- 将原本src内容清空,替换成教学资料的《vuex-cart-准备代码》
需求:
- 发请求动态渲染购物车,数据存vuex (存cart模块, 将来还会有user模块,article模块…)
- 数字框可以修改数据
- 动态计算总价和总数量
8 综合案例-构建vuex-cart模块
- 新建
store/modules/cart.js
export default { namespaced: true, state () { return { list: [] } }, }
- 挂载到 vuex 仓库上
store/cart.js
import Vuex from 'vuex' import Vue from 'vue' import cart from './modules/cart' Vue.use(Vuex) const store = new Vuex.Store({ modules: { cart } }) export default store
9 综合案例-准备后端接口服务环境
- 安装全局工具 json-server (全局工具仅需要安装一次)
yarn global add json-server 或 npm i json-server -g
- 代码根目录新建一个 db 目录
- 将资料 index.json 移入 db 目录
- 进入 db 目录,执行命令,启动后端接口服务 (使用–watch 参数 可以实时监听 json 文件的修改)
json-server --watch index.json
10 综合案例-请求动态渲染数据
1.目标
请求获取数据存入 vuex, 映射渲染
- 安装 axios
yarn add axios
- 准备actions 和 mutations
import axios from 'axios' export default { namespaced: true, state () { return { list: [] } }, mutations: { updateList (state, payload) { state.list = payload } }, actions: { async getList (ctx) { const res = await axios.get('http://localhost:3000/cart') ctx.commit('updateList', res.data) } } }
App.vue
页面中调用 action, 获取数据
import { mapState } from 'vuex' export default { name: 'App', components: { CartHeader, CartFooter, CartItem }, created () { this.$store.dispatch('cart/getList') }, computed: { ...mapState('cart', ['list']) } }
- 动态渲染
<!-- 商品 Item 项组件 --> <cart-item v-for="item in list" :key="item.id" :item="item"></cart-item>
cart-item.vue
<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 class="btn btn-light">-</button> <span class="count">{{item.count}}</span> <button class="btn btn-light">+</button> </div> </div> </div> </div> </template> <script> export default { name: 'CartItem', props: { item: Object }, methods: { } } </script>
11 综合案例-修改数量
- 注册点击事件
<!-- 按钮区域 --> <button class="btn btn-light" @click="onBtnClick(-1)">-</button> <span class="count">{{item.count}}</span> <button class="btn btn-light" @click="onBtnClick(1)">+</button>
- 页面中dispatch action
onBtnClick (step) { const newCount = this.item.count + step if (newCount < 1) return // 发送修改数量请求 this.$store.dispatch('cart/updateCount', { id: this.item.id, count: newCount }) }
- 提供action函数
async updateCount (ctx, payload) { await axios.patch('http://localhost:3000/cart/' + payload.id, { count: payload.count }) ctx.commit('updateCount', payload) }
- 提供mutation处理函数
mutations: { ..., updateCount (state, payload) { const goods = state.list.find((item) => item.id === payload.id) goods.count = payload.count } },
12 综合案例-底部总价展示
- 提供getters
getters: { total(state) { return state.list.reduce((p, c) => p + c.count, 0); }, totalPrice (state) { return state.list.reduce((p, c) => p + c.count * c.price, 0); }, },
- 动态渲染
<template> <div class="footer-container"> <!-- 中间的合计 --> <div> <span>共 {{total}} 件商品,合计:</span> <span class="price">¥{{totalPrice}}</span> </div> <!-- 右侧结算按钮 --> <button class="btn btn-success btn-settle">结算</button> </div> </template> <script> import { mapGetters } from 'vuex' export default { name: 'CartFooter', computed: { ...mapGetters('cart', ['total', 'totalPrice']) } } </script>