针对的知识点
- 组件的基本使用
- 组件间的数据交互(父子组件传值,兄弟组件传值)
- 组件插槽
- 购物车案例
原始案例html结构
<div class="container">
<div class="title">我的商品</div>
<div>
<div class="item">
<img src="img/a.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/b.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/c.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/d.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/e.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
</div>
<div class="total">
<span>总价:123</span>
<button>结算</button>
</div>
</div>
原始css样式
<style type="text/css">
.container {
width: 300px;
margin: auto;
}
.container .cart {
width: 300px;
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
/*color: #fff;*/
}
.container .total {
background-color: #FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: #DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid #ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;
left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration: none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
提供的数据
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
下面使用vue重构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.container {
width: 300px;
margin: auto;
}
.container .cart {
width: 300px;
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
/*color: #fff;*/
}
.container .total {
background-color: #FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: #DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid #ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;
left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration: none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<cart-shop></cart-shop>
</div>
</div>
<script type="text/javascript" src="../vue.js"></script>
<script type="text/javascript">
var cartTitle = {
props: ['uname'],
// 标题组件:父组件向子组件传值,子组件将数据显示在页面
template: `<div class="title">{{uname}}的商品</div>`
}
var cartCon = {
props: ['list'],
template: `
<div>
<div class="item" :key='item.id' v-for='(item,index) in list'>
<img :src=item.img />
<div class="name">{{item.name}}</div>
<div class="change">
<a href="" @click='sub(item.id)'>-</a>
<input type="text" class="num" :value='item.num' @blur='change(item.id,$event)' />
<a href="" @click.prevent='add(item.id)'>+</a>
</div>
<div class="del" @click.prevent='del(item.id)'>×</div>
</div>
</div>
`,
// 实现组件跟新数据上, 将默认数据先渲染, 输入框失去焦点的时候, 改变商品的数量
// 子组件不推荐直接操作父组件的数据,建议将数据传给父组件
methods: {
// 修改数据
change: function(id, event) {
this.$emit('num-mo', {
id: id,
num: event.target.value,
type: 'change'
})
},
// 数量的增加和减少, 通过父组件来计算, 每次都是加一减一, 不需要传递数量,
// 父组件需要一个类型来判断, 通过type表示来标记不同的操作
sub: function(id) {
this.$emit('num-mo', {
id: id,
type: 'sub'
})
},
add: function(id) {
this.$emit('num-mo', {
id: id,
type: 'add'
})
},
// 删除的时候, 需要把删除的id传过来
// 子组件不推荐直接操作父组件的数据吗, 有可能其他子组件公用父组件的数据, 我们需要把数据传递给父组件
// 让父组件删除对应的数据
del: function(id) {
console.log(id);
// 子组件通过$emit绑定自定义事件,并且传入id
this.$emit('car-id', id)
console.log('sss');
}
},
}
// 实现按钮的删除功能
var cartTotal = {
props: ['total'],
data: function() {
return {
}
},
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`
}
Vue.component('cart-shop', {
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-con :list='list' @car-id='dele($event)' @num-mo='changeNum($event)'></cart-con>
<cart-total :total='total'></cart-total>
</div>
`,
// 父组件用子组件自定义的事件,做事件名,用$event来接收传入的id
components: {
'cart-title': cartTitle,
'cart-con': cartCon,
'cart-total': cartTotal
},
methods: {
dele: function(id) {
console.log('123112');
var index = this.list.findIndex(item => {
return item.id == id
})
console.log('123');
this.list.splice(index, 1)
},
changeNum: function(val) {
if (val.type == 'change') {
this.list.some(item => {
if (item.id == val.id) {
item.num = val.num
return true
}
})
} else if (val.type == 'sub') {
this.list.some(item => {
if (item.id == val.id) {
item.num -= 1
return true
}
})
} else if (val.type == 'add') {
this.list.some(item => {
if (item.id == val.id) {
item.num += 1;
return true
}
})
}
},
},
computed: {
total: function() {
var t = 0;
this.list.forEach(item => {
t += item.price * item.num
});
return t
}
},
})
var vm = new Vue({
el: '#app',
data: {}
});
function a() {}
console.dir(a);
</script>
</body>
</html>
要点说明
- 商品管理主要分三部分进行组件化开发,头部标题,中间内容主题区,底部总价,利用组件化开发,拆分成三个组件,有些操作,在子组件不能操作,需要在父组件传值,所以这是重点
- 子组件 var carT = {
template:组件内容
,
} - 全局父组件 Vue.component = {‘car-all’,{template:<div
<car-t></car-t></div>
},components:{cat-t
,carT}} - 改造完父子组件,开始数据的渲染,现在是组件化开发,组件数据,需要写在父组件里面。然后对应的子组件,通过循环的方式,将数据展示在页面
父组件通过属性绑定的方式将值传给子组件,子组件通过props来接收父组件的传值
- 标题文字的改变也是通过父组件传值 组件内部通过props来接收传递来的值,父组件通过属性绑定的方式传递给子组件:uname=‘uname’, propos:[‘uname’]
- 计算总价 利用计算属性 ,循环当前数组 forEach
- 删除操作 获取id,利用子组件向父组件传值的方式,的方式,子组件用 e m i t ( ) 参 数 1 : 自 定 义 事 件 , 参 数 2 : 数 值 , 父 组 件 用 自 定 义 事 件 当 成 自 己 的 事 件 , 用 emit()参数1:自定义事件,参数2:数值,父组件用自定义事件当成自己的事件,用 emit()参数1:自定义事件,参数2:数值,父组件用自定义事件当成自己的事件,用event接收参数 。利用findIndex,来查找当前id的索引,利用splice()来删除数据
- 数据的在表单的显示 :value=‘item.num’ 给输入框绑定@blur事件,传递数据,传递数据以后,将数据重新赋值给id,num event.target.value
- 在数据加一,减一,通过类型来判断,this.list…some(item=<{if (item.id == val.id)} item.num = val.id return true )
要点:
父组件向子组件传值
组件内部通过props接收传递来的值
父组件通过属性将值传递给子组件
子组件向父组件传值
子组件通过自定义事件向父组件传递信息
父组件监听子组件的事件
效果图