一.心得体会图_传单个的属性和直接传递一个对象的区别:

image.png
总结:以前封装组件的时候喜欢把一个对象直接放进去,后面才发现这样是不可复用的,因为两个组件传递对象过来到你封装的组件内,属性如果要是一致的话,就没啥问题,
要是A传递过来的是: Obj.goods_name,
要是B传递过来的是: Obj.cx_name,
你说在自己封装的组件中,你该怎么去绑定从父组件中传递过来的值呢?
所以说封装的组件,你还是要按照单个属性的这种传值,这样就会有效的提高复用性
二.完整封装一个组件
二.一 定义一个父组件App.vue
<template>
<div id="app">
<!-- 这个可以直接把goodsList数组传递过去,但是不推荐,想要有课复用性,就传单个的属性过去 -->
<CartDataile
v-for="item in goodsList"
:key="item.id"
:id="item.id"
:price="item.price"
:checked="item.check"
:imgurl="item.image_url"
:goodstitle="item.goods_title"
@change="change"
></CartDataile>
</div>
</template>
<script>
import CartDataile from "@/components/CartDataile.vue";
export default {
name: "App",
data() {
return {
goodsList: [
{
id: 1,
price: 180.11,
check: true,
goods_name: "风衣",
goods_title: "风衣商品描述",
imageurl:
"https://img1.baidu.com/it/u=3612402013,982327803&fm=26&fmt=auto",
},
{
id: 2,
price: 580.12,
check: false,
goods_name: "卫衣",
goods_title: "卫衣商品描述",
image_url:
"https://img0.baidu.com/it/u=1631902795,2594006009&fm=224&fmt=auto&gp=0.jpg",
},
{
id: 3,
price: 120.96,
check: true,
goods_name: "短袖",
goods_title: "短袖商品描述",
image_url:
"https://img1.baidu.com/it/u=457792117,1201850547&fm=26&fmt=auto",
},
{
id: 4,
price: 380.78,
check: false,
goods_name: "整套女装",
goods_title: "整套女装商品描述",
image_url:
"http://t15.baidu.com/it/u=1024874080,297428470&fm=224&app=112&f=JPEG?w=500&h=500",
},
],
};
},
components: {
CartDataile,
},
methods: {
change(e) {
console.log(e);
}
},
};
</script>
<style lang="less" scoped>
</style>
二.二封装一个组件CartDetail.vue
<template>
<div class="detaile-box">
<div class="detail-item">
<div class="ipt-box">
<input
type="checkbox"
name="goods"
:checked="checked"
@change="change"
/>
</div>
<div class="left-box">
<img :src="imgurl" alt="" />
</div>
<div class="right-box">
<div class="title_info">{{ goodstitle }}</div>
<div class="price">¥ {{ price }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
id: {
required: true,
type: Number,
},
imgurl: {
type: String,
default:
"https://img1.baidu.com/it/u=3612402013,982327803&fm=26&fmt=auto",
},
price: {
type: Number,
default: 0,
},
goodstitle: {
type: String,
default: "",
},
checked: {
checked: Boolean,
default: false,
},
},
methods: {
change(e) {
const id = this.id;
const newState = e.target.checked;
this.$emit("change", { id, newState });
},
},
};
</script>
<style lang="less" scoped>
.detaile-box {
width: 100%;
.detail-item {
background-color: #ddd;
width: 100%;
display: flex;
height: 200px;
.ipt-box {
line-height: 200px;
vertical-align: middle;
input[type="radio"],
input[type="checkbox"] {
zoom: 180%;
color: blue;
}
}
.left-box {
flex: 1;
margin-right: 5px;
border-right: 1px solid #eee;
vertical-align: middle;
img {
height: 180px;
}
}
.right-box {
flex: 1;
position: relative;
.title_info {
font-weight: bold;
font-size: 18px;
}
.price {
color: red;
position: absolute;
bottom: 0;
left: 10px;
font-size: 18px;
font-weight: bold;
}
}
}
}
</style>
二.三 渲染的实际截图:

image.png
三.怎么在子组件发射事件给父组件具体的应用场景:
三.一 分析流程如下:

image.png
三.二具体代码实现:

image.png
/*************以下是完成功能的封装******************/
父组件App.vue
<template>
<div id="app">
<!-- 头部区域 -->
<CartHeader :title="title"></CartHeader>
<!-- 这个可以直接把goodsList数组传递过去,但是不推荐,想要有课复用性,就传单个的属性过去 -->
<CartDataile
v-for="item in goodsList"
:key="item.id"
:id="item.id"
:price="item.price"
:checked="item.check"
:imgurl="item.image_url"
:goodstitle="item.goods_title"
:count="item.count"
@change="change"
></CartDataile>
<!-- 底部区域 -->
<!-- :allp="allp" -->
<CartFooter :checked="fullState" @changeFull="changeFull" :allp="allp" :jiesuan="jiesuan"></CartFooter>
</div>
</template>
<script>
import CartHeader from "@/components/CartHeader.vue";
import CartDataile from "@/components/CartDataile.vue";
import CartFooter from "@/components/CartFooter.vue";
export default {
name: "App",
components: {
CartHeader,
CartDataile,
CartFooter,
},
data() {
return {
title: "购物车案例",
goodsList: [
{
id: 1,
price: 180.11,
check: true,
count: 1,
goods_name: "风衣",
goods_title: "风衣商品描述",
imageurl:
"https://img1.baidu.com/it/u=3612402013,982327803&fm=26&fmt=auto",
},
{
id: 2,
price: 580.12,
check: false,
count: 2,
goods_name: "卫衣",
goods_title: "卫衣商品描述",
image_url:
"https://img0.baidu.com/it/u=1631902795,2594006009&fm=224&fmt=auto&gp=0.jpg",
},
{
id: 3,
price: 120.96,
check: true,
count: 4,
goods_name: "短袖",
goods_title: "短袖商品描述",
image_url:
"https://img1.baidu.com/it/u=457792117,1201850547&fm=26&fmt=auto",
},
{
id: 4,
price: 380.78,
check: false,
count: 3,
goods_name: "整套女装",
goods_title: "整套女装商品描述",
image_url:
"http://t15.baidu.com/it/u=1024874080,297428470&fm=224&app=112&f=JPEG?w=500&h=500",
},
],
};
},
computed: {
// 计算全选的状态是true 还是false
fullState() {
const state = this.goodsList.every((item) => item.check);
console.log(state);
return state;
},
// 计算总共用了多少钱
allp() {
const allprice = this.goodsList
.filter((item) => item.check)
.reduce((proV, item) => (proV + item.price * item.count), 0);
return allprice;
},
// 结算
jiesuan() {
const newLength = this.goodsList.filter(item=> item.check).length
return newLength;
}
},
methods: {
change(e) {
console.log(e);
this.goodsList.some((item) => {
if (item.id === e.id) {
item.check = e.value;
return true;
}
});
},
// 接收Footer子组件传递过来的事件
changeFull(e) {
this.goodsList.forEach((item) => (item.check = e));
},
},
};
</script>
<style lang="less" scoped>
</style>
头部组件CartHeader.vue
<template>
<div class="header-box">
<div class="header-detail">
<p>{{ title }}</p>
</div>
</div>
</template>
<script>
export default {
props:{
title:{
type:String,
default: "头部信息"
}
}
}
</script>
<style lang="less" scoped>
.header-box {
position: relative;
.header-detail {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 46px;
background: rgb(18,121,255);
color: #fff;
text-align: center;
line-height: 46px;
z-index: 9999;
}
}
</style>
CartDataile.vue详情组件
<template>
<div class="detaile-box">
<div class="detail-item">
<div class="ipt-box">
<input
type="checkbox"
name="goods"
:checked="checked"
@change="change"
/>
</div>
<div class="left-box">
<img :src="imgurl" alt="" />
</div>
<div class="right-box">
<div class="title_info">{{ goodstitle }}</div>
<div class="price">¥ {{ price }}</div>
<div class="count">总件数 X {{ count }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
id: {
required: true,
type: Number,
},
imgurl: {
type: String,
default:
"https://img1.baidu.com/it/u=3612402013,982327803&fm=26&fmt=auto",
},
count:{
type: Number,
default: 0
},
price: {
type: Number,
default: 0,
},
goodstitle: {
type: String,
default: "",
},
checked: {
type: Boolean,
default: false,
},
},
methods: {
change(e) {
const id = this.id;
const newState = e.target.checked;
this.$emit("change", { id, value: newState });
},
},
};
</script>
<style lang="less" scoped>
.detaile-box {
width: 100%;
.detail-item {
background-color: #ddd;
width: 100%;
display: flex;
height: 200px;
margin-top: 50px;
margin-bottom: 50px;
.ipt-box {
line-height: 200px;
vertical-align: middle;
input[type="radio"],
input[type="checkbox"] {
zoom: 180%;
color: blue;
}
}
.left-box {
flex: 1;
margin-right: 5px;
border-right: 1px solid #eee;
vertical-align: middle;
img {
height: 180px;
}
}
.right-box {
flex: 1;
position: relative;
.title_info {
font-weight: bold;
font-size: 18px;
}
.price {
color: red;
position: absolute;
bottom: 0;
left: 10px;
font-size: 18px;
font-weight: bold;
}
.count {
color: #333;
font-size: 12px;
}
}
}
}
</style>
底部组件CartFooter.vue
<template>
<div class="footer-box">
<div class="footer-detail">
<label for="ipt">
全选
<input
id="ipt"
class="left"
type="checkbox"
name="goods"
:checked="checked"
@change="change"
/>
</label>
<span class="center">总价: {{ allp.toFixed(2) }}</span>
<span class="right">结算: {{ jiesuan }}件</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
change(e) {
const value = e.target.checked;
console.log(value);
this.$emit("changeFull", value);
},
},
props: {
checked: {
type: Boolean,
default: false,
},
allp: {
type: Number,
default: 0,
},
jiesuan: {
type: Number,
default: 0,
},
},
};
</script>
<style lang="less" scoped>
.footer-box {
position: relative;
.footer-detail {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 46px;
background: #fff;
color: #fff;
line-height: 46px;
z-index: 9999;
display: flex;
padding: 0 20px;
color: red;
font-weight: bold;
justify-content: space-between;
.left {
}
.center {
color: #333;
}
.right {
display: inline-block;
margin-top: 5px;
color: #fff;
width: 80px;
height: 30px;
border-radius: 15px;
text-align: center;
line-height: 30px;
background: rgb(18, 121, 255);
}
}
}
</style>
实际截图:

image.png
网友评论