目标: 功能分析,创建项目,构建分析基本结构
一. 功能模块分析
(1). 请求动态渲染购物车,数据存 vuex
(2). 数字框控件 修改数据
(3). 动态计算 总价和总数量
二. 脚手架新建项目
(1). 创建项目指令
# 黑窗口执行,在那个文件下,项目就会建在那(新建项目)
vue create vue-cart-demo(项目名)
(2). 创建项目流程
三.综合案例-购物车
目标:构建cart 购物车模块
说明:既然明确数据要存 vuex,建议分模块存,购物车数据存 cart 模块,将来还会有 user 模块,article 模块...
1. 创建子组件
(1). cart-header.vue
<template>
<div>header</div>
</template>
(2). cart-item.vue
<template>
<div class="goods-container">
<!-- 左侧图片区域 -->
<div class="left">
<img src="" class="avatar">
</div>
<!-- 右侧商品区域 -->
<div class="right">
<!-- 标题 -->
<div class="title">标题</div>
<div class="info">
<!-- 单价 -->
<span class="price">$10</span>
<div class="btns">
<!-- 按钮区域 -->
<button class="btn btn-light">-</button>
<span class="count">1</span>
<button class="btn btn-light">+</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CartItem',
}
</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{
margin-left: 15px;
.title{
margin-top: 10px;
font-size: 15px;
}
.info{
margin-top: 50px;
.price{
float: left;
}
.btns{
float: right;
margin-left: 30px;
.count{
margin-left: 5px;
margin-right: 5px;
}
}
}
}
}
</style>
(3). cart-footer.vue
<template>
<div class="footer-container">
<!-- 中间的合计 -->
<div>
<span>共 件商品,合计:</span>
<span class="price">¥ </span>
</div>
<!-- 右侧结算按钮 -->
<button class="btn btn-success btn-settle">结算</button>
</div>
</template>
<script>
export default {
name: 'CartFooter'
}
</script>
<style lang="less" scoped>
.footer-container{
background-color: white;
height: 50px;
border-top: 1px solid #f8f8f8;
display: flex;
.btn{
height: 20px;
margin-left: 20%;
background-color:blue;
}
}
</style>
2. App.vue引入子组件
<template>
<div class="app-container">
<!-- Header区域 -->
<cart-header></cart-header>
<!-- 商品 Item 项组件 -->
<cart-item></cart-item>
<cart-item></cart-item>
<cart-item></cart-item>
<!-- Foote 区域 -->
<cart-footer></cart-footer>
</div>
</template>
<script>
import CartHeader from '@/components/cart-header.vue'
import CartFooter from '@/components/cart-footer.vue'
import CartItem from '@/components/cart-item.vue'
export default {
name: 'App',
components: {
CartHeader,
CartFooter,
CartItem
}
}
</script>
<style lang="less" scoped>
.app-container{
padding: 50px 0;
font-size:14px
}
</style>
3. 构建购物车模块状态
4. 准备后端接口服务环境
目标:基于json-server 工具,准备后端接口服务环境
(1).安装 json-server
// 安装全局工具json-server (全局工具仅需要安装一次)
npm i json-server -g
(2).根目录新建 db 目录
// 项目根目录新建一个 db 目录,新建index.json存放数据
{
"cart":[
{
"id":100001,
"name":"牛皮纤维纸",
"price":128,
"count":1,
"thumb":"https://img2.baidu.com/it/u=2936117222,3860513461&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1695574800&t=047a0ddf535ee76b5e43f5d31636a61e"
},
{
"id":100002,
"name":"牛皮纤维纸",
"price":128,
"count":1,
"thumb":"https://img2.baidu.com/it/u=2936117222,3860513461&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1695574800&t=047a0ddf535ee76b5e43f5d31636a61e"
},
{
"id":100003,
"name":"牛皮纤维纸",
"price":128,
"count":1,
"thumb":"https://img2.baidu.com/it/u=2936117222,3860513461&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1695574800&t=047a0ddf535ee76b5e43f5d31636a61e"
},
{
"id":100004,
"name":"牛皮纤维纸",
"price":128,
"count":1,
"thumb":"https://img2.baidu.com/it/u=2936117222,3860513461&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1695574800&t=047a0ddf535ee76b5e43f5d31636a61e"
},
{
"id":100005,
"name":"牛皮纤维纸",
"price":128,
"count":1,
"thumb":"https://img2.baidu.com/it/u=2936117222,3860513461&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1695574800&t=047a0ddf535ee76b5e43f5d31636a61e"
}
],
"friends":[
{"id":1,"name":"zhangsan","age":18},
{"id":2,"name":"lisi","age":19},
{"id":3,"name":"wangwu","age":20}
]
}
(3).启动后端接口服务
// 进入db 目录,执行命令,启动后端接口服务
json-server index.json
(4).访问接口测试
http://localhost:3000/cart
5. vuex请求数据,映射渲染
目标:请求获取数据存入vuex映射渲染
(1). 安装axios
npm i axios
// 如果报错就执行以下代码安装axios
// npm i axios --legacy-peer-deps
(2). cart.js 请求接口
// cart.js 请求获取数据存入vuex映射渲染
state: { 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)
}
}
(3). 页面调用 action
$store.dispatch('模块名/xxx')
(4). 代码示例
6. 修改数量功能
目标: 修改数量功能完成
注意: 前端vuex数据,后端数据库数据都要更新
mutations: {
updateCount (state, payload) {
const goods = state.list.find(item => item.id === payload.id)
goods.count = payload.count
}
},
actions: {
async UpdateCountAsync (ctx,payload) {
await axios.patch('http://localhost:3000/cart' + payload.id, {
count: payload.count
})
context.commit('updateCount', payload)
}
},
(1). 代码示例
7. 底部 getter 统计
目标:底部 getters 统计
(1). 提供 getters
getter:{
total(state){
return state.list.reduce((sum,item) => sum + item.count, 0)
},
totalPrice(state){
return state.list.reduce((sum,item) => sum + item.count * item.price,0)
}
}
(2). 使用getters
computed: {
...mapGetters('cart', ['total', 'totalPrice'])
}