文章目录
关于购物车案例的实现
一般来说,实现购物车案例依赖的是大量的父子组件之间数据的相互传递
那么Vue3.0的组合式api和Vue2.0的选项式api关于父子组件通信有哪些不同呢?
- 如果是父组件向子组件进行传递数据
-
在选项式 API 中
a. 我们可以提供props选项来声明接收传递的数据
b. 在JS中可使用this.$props来访问声明的自定义的属性
c. 在视图模板中,可直接访问props中声明的自定义属性 -
在组合式 API 中
a. 我们可以采用defineProps来声明接收传递的数据
b. 在JS中可使用defineProps返回的对象来访问声明的自定义的属性
c. 在视图模板中,可直接访问defineProps中声明的自定义属性
- 如果是子组件向父组件进行传递数据
-
子组件声明自定义的事件
- 在选项式 API 中,子组件可通过emits选项来声明自定义的事件
- 在组合式 API 中,子组件可通过defineEmits()宏来声明自定义的事件
-
子组件中触发自定义事件(可传值)
在选项式 API 中,可通过组件当前实例this.$emit(event, …args)来触发当前组件自定义的事件
在组合式 API 中,可调用defineEmits宏返回的emit(event, …args)函数来触发当前组件自定义的事件
其中上方两个参数分别为:
● event:触发事件名,字符串类型
● …args:传递参数,可没有,可多个 -
父组件使用子组件时监听对应的自定义事件,并执行父组件中的函数(获取子组件传递的值)
使用v-on:event="callback"或者@event="callback"来监听子组件是否触发了该事件
(1).event:事件名字(camelCase 形式命名的事件,在父组件中可以使用kebab-case形式来监听)
(2) callback:回调函数,如果子组件触发该事件,那么在父组件中执行对应的回调函数,回调函数声明参数可自动接收到触发事件传来的值
代码实现
- App.vue
<template>
<div>
<!-- 产品展示 -->
<Product
v-for="product in shopCar"
:key="product.id"
:id="product.id"
:picture="product.picture"
:title="product.title"
:subtitle="product.subtitle"
:image="product.image"
:price="product.price"
:count="product.count"
:is-checked="product.selected"
@change-product-count="productCount"
@change-product-checked="productChecked"
>
</Product>
<hr />
<!-- 控制器 -->
<Collect
:is-all-checked="isSelectProduct"
@change-all-checked-state="AllProductChecked"
:all-total="total"
:all-count="AllCount"
></Collect>
</div>
</template>
<script setup>
import Product from "@/views/gwc/Product.vue";
import Collect from "@/views/gwc/Collect.vue";
import { computed, ref } from "vue";
let shopCar = ref(
// 购物车信息
[
{
id: 89,
title:
"四川爱媛38号果冻橙 当季时令应季彩箱装甜桔橘子新鲜水果专区 净重2斤小果尝鲜装(力荐大果,口感更好更实惠)",
subtitle:
"由初逐旗舰店从 四川眉山市 发货, 并提供售后服务. 现在至明日17:00前完成下单,预计11月15日19:30前发货",
image:
"https://img12.360buyimg.com/n1/jfs/t1/39198/22/19565/188868/634a3bc4Ea15f2eee/2bb232b36cdd285c.jpg",
price: 10,
count: 1,
selected: false,
},
{
id: 102,
title:
"【现货速发】新鲜四季青柠檬 无籽香水柠檬当季生鲜小青柠檬奶茶店水果 有籽青柠檬1斤装试吃【50-80克】",
subtitle:
"由朵艾美水果旗舰店发货, 并提供售后服务. 现在至明日16:00前完成下单,预计11月16日23:30前发货",
image:
"https://img12.360buyimg.com/n1/jfs/t1/191077/5/6346/108268/60beea0dEc3a6d2ad/15db7dd619a0bc4f.jpg",
price: 9,
count: 3,
selected: true,
},
{
id: 108,
title:
"新疆阿克苏冰糖心苹果 新鲜时令水果 阿克苏苹果红富士 10斤礼盒装 单果75-85mm 净重9斤多",
subtitle:
"由阿克苏苹果旗舰店发货, 并提供售后服务. 现在至明日16:00前完成下单,预计11月16日20:30前发货",
image:
"https://img13.360buyimg.com/n1/jfs/t1/64647/33/22918/106322/6360afb1E9bab1003/a82bda0aeae6e953.png",
price: 80,
count: 2,
selected: false,
},
]
);
//修改单个商品数量 参数 数量 iD
function productCount(count, id) {
// 寻找具体是哪个商品 some循环
shopCar.value.some((product) => {
// 判断更改的是哪个商品
if (id === product.id) {
product.count += count;
return true; // 找到即停止循环
}
});
}
// 修改单个商品的选中状态
function productChecked(checked, id) {
// 寻找具体是哪个商品 some循环
shopCar.value.some((product) => {
// 判断更改的是哪个商品
if (id === product.id) {
product.selected = checked;
return true; // 找到即停止循环
}
});
}
// 改变所有商品的选中状态
function AllProductChecked(checked) {
console.log(checked);
shopCar.value.forEach((product) => (product.selected = checked));
}
// 当前组件内computed可以和data中数据一样 能直接拿来用
// 是否全选
let isSelectProduct = computed(() => {
// every
return shopCar.value.every((product) => product.selected);
});
// 计算总金额
let total = computed(() => {
return shopCar.value
.filter((item) => item.selected) // 过滤掉被选中得产品
.reduce((money, item) => (money += item.price * item.count), 0); // 累加器
});
// 总价和总数量写在根组件是因为:
// 1.子组件修改不了父组件传过来的数据 因为是单向数据流
// 2.父组件参数比较好修改
// 计算总数量
let AllCount = computed(() => {
return shopCar.value
.filter((item) => item.selected)
.reduce((allcount, item) => (allcount += item.count), 0);
});
</script>
- Product.vue
<template>
<!-- 产品容器 -->
<div class="box">
<!-- 选项框 -->
<input
type="checkbox"
class="p_checkbox"
:checked="isChecked"
@change="changeCheckState"
/>
<!-- 产品图 -->
<img :src="image" alt="" class="p_image" />
<!-- 产品内容 -->
<div class="p_content">
<!-- 标题 -->
<h3 class="p_title" v-text="title"></h3>
<!-- 副标题 -->
<span class="p_subtitle" v-text="subtitle"></span>
<!-- 价格 -->
<h2 class="p_price">¥ {{ price }}</h2>
<!-- 数量区域 -->
<div class="p_count_area">
<!-- disabled 禁用 -->
<button
:disabled="count <= 1"
@click="emits('changeProductCount', -1, id)"
>
-
</button>
<!-- 商品的数量 -->
<span v-text="count"></span>
<button @click="emits('changeProductCount', 1, id)">+</button>
</div>
</div>
</div>
</template>
<script setup>
let propsData = defineProps({
id: { type: Number, required: true },
title: { type: String, required: true },
subtitle: { type: String, required: true },
price: { type: Number, required: true, default: 0 },
count: { type: Number, required: true, default: 0 },
isChecked: Boolean,
image: { type: String, required: true },
});
//声明 自定义事件
let emits = defineEmits([
"changeProductChecked", //改变复选框
"changeProductCount", //改变产品数量
]);
// 改变商品的选中状态
function changeCheckState(e) {
// 目标源
// console.log(e);
let newCheckState = e.target.checked;
emits("changeProductChecked", newCheckState, propsData.id);
}
</script>
<style>
.box {
box-shadow: 0 0 8px gray;
padding: 20px;
margin: 15px;
display: flex;
align-items: center;
}
.p_checkbox {
width: 25px;
height: 25px;
}
.p_image {
width: 120px;
height: 120px;
margin: 0 20px;
}
.p_content {
align-self: start;
width: 100%;
position: relative;
}
.p_title {
margin-bottom: 8px;
}
.p_subtitle {
font-size: 14px;
color: gray;
}
.p_price {
margin-top: 20px;
color: rgb(201, 67, 67);
}
.p_count_area {
position: absolute;
bottom: 0;
right: 0;
}
</style>
- Collect.vue
<template>
<div class="container">
<label>
<!-- label 受击面积更大 -->
<!-- 全选按钮 -->
<input
type="checkbox"
:checked="isAllChecked"
@change="emits('changeAllCheckedState', $event.target.checked)"
/>
全选
</label>
<!-- 总金额 -->
<span
>合计:
<strong>¥ {{ allTotal }}</strong>
</span>
<!-- 数量 -->
<span>
数量:
<strong>{{ allCount }}</strong>
</span>
</div>
</template>
<script setup>
defineProps({
isAllChecked: Boolean, // 是否全选
allTotal: { type: Number, default: 0 }, // 总金额
allCount: { type: Number, default: 0 }, // 总数量
});
let emits = defineEmits(["changeAllCheckedState"]);
</script>
<style>
.container {
padding: 20px;
margin: auto 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.container input {
width: 25px;
height: 25px;
}
.container label {
display: flex;
align-items: center;
width: 70px;
justify-content: space-between;
}
.container strong {
color: red;
}
.container button {
border: none;
padding: 15px 25px;
background-color: rgb(50, 140, 192);
color: white;
border-radius: 8px;
box-shadow: 0 0 5px gray;
}
</style>
效果演示
文章总结
简单归纳了Vue3.0 组合式api实现购物车案例的与Vue2.0 组合式api的几点区别,并用代码实现了既定效果,希望能对你有所帮助。
1634

被折叠的 条评论
为什么被折叠?



