Vue.js之简单购物车的实现
该组件作为父组件,内部又划分为三个组件,分别为cart-title、cart-list、cart-total,对应购物车的标题、列表以及总价区域。为两个按钮绑定点击事件,为输入框绑定失去焦点事件。在父组件中定义list数据,通过props传给总价组件,在计算属性中利用reduce方法得出总价。在父组件中定义uname数据,通过props属性传给标题组件。商品列表组件功能的实现是购物车最重要的部分,
目录
本文主要介绍如何利用Vue.js组件化实现简单的购物车案例,实现效果如图:
1.静态布局
通过HTML和CSS实现静态布局,效果实现如图所示。
2.组件划分和封装
(1) 组件划分
购物车总体为一个组件,为my-cart:
<div id="app">
<my-cart></my-cart>
</div>
该组件作为父组件,内部又划分为三个组件,分别为cart-title、cart-list、cart-total,对应购物车的标题、列表以及总价区域。
Vue.component('my-cart', {
template: ``,
components: {
'cart-title': cartTitle,
'cart-list': cartList,
'cart-total': cartTotal
}
})
(2)组件封装
划分完成后,则将静态布局的代码放置到对应组件的模板中。
my-cart父组件中template的内容为:
template: `<div id="cart">
<cart-title></cart-title>
<cart-list></cart-list>
<cart-total></cart-total>
</div>`
cart-title中template的内容:
template: ` <div class="title">XXX的商品</div>`
cart-list中template的内容:
template: `<ul>
<li><span>手机</span><span>×</span>
<div class="btn"><button>-</button><input type="text"><button>+</button></div>
</li>
<li><span>电脑</span><span>×</span>
<div class="btn"><button>-</button><input type="text"><button>+</button></div>
</li> <li><span>耳机</span><span>×</span>
<div class="btn"><button>-</button><input type="text"><button>+</button></div>
</li> <li><span>充电宝</span><span>×</span>
<div class="btn"><button>-</button><input type="text"><button>+</button></div>
</li>
</ul>`,
cart-total中template的内容:
template: ` <div class="footer">
<span>总价:<span>XXX</span></span>
<span>结算</span>
</div>`,
3.功能实现
(1)标题组件
功能:将用户名显示于标题栏。
实现:
在父组件中定义uname数据,通过props属性传给标题组件。
let cartTitle = {
props: ['uname'],
template: ` <div class="title">{{uname}}的商品</div>`
}
(2)总价组件
在父组件中定义list数据,通过props传给总价组件,在计算属性中利用reduce方法得出总价。
let cartTotal = {
props: ['list'],
template: ` <div class="footer">
<span>总价:<span>{{sum}}</span></span>
<span>结算</span>
</div>`,
computed: {
sum() {
let sum = this.list.reduce((total, e) => {
return total + e.price * e.num;
}, 0)
return sum;
}
}
}
(3)商品列表组件
商品列表组件功能的实现是购物车最重要的部分,相对来说功能较复杂。
a.首先渲染数据,定义以下模板
template: `<ul>
<li :key="item.id" v-for="(item,index) in list"><span>{{item.name+' 价格: '+item.price}}</span><span>×</span>
<div class="btn"><button>-</button><input type="text"><button>+</button></div>
</li>
</ul>`,
b.左右点击按钮以及中间输入框的功能
为两个按钮绑定点击事件,为输入框绑定失去焦点事件。同时为三者绑定同一个自定义事件,后面通过传过去的数据判定具体进行何种操作。
let cartList = {
props: ['list'],
template: `<ul>
<li :key="item.id" v-for="(item,index) in list"><span>{{item.name+' 价格: '+item.price}}</span><span @click="del(index)">×</span>
<div class="btn"><button @click="sub(index)">-</button><input type="text" :value="item.num" @blur="changeNum(index,$event)"><button @click="add(index)">+</button></div>
</li>
</ul>`,
methods: {
del(i) {
this.$emit('cart-del', i);
},
changeNum(i, e) {
this.$emit('goods-num', {
id: i,
num: e.target.value,
type: 'inputChange'
})
},
sub(i) {
this.$emit('goods-num', {
id: i,
type: 'subChange'
})
},
add(i) {
this.$emit('goods-num', {
id: i,
type: 'addChange'
})
}
}
}
在父组件中绑定自定义的监听事件
template: `<div id="cart">
<cart-title :uname="uname"></cart-title>
<cart-list :list="list" @cart-del="del($event)" @goods-num="changeNum($event)"></cart-list>
<cart-total :list="list"></cart-total>
</div>`
最后在父组件中定义方法
changeNum(e) {
if (e.type == 'inputChange') {
this.list[e.id].num = e.num;
} else if (e.type == 'addChange') {
this.list[e.id].num++;
} else {
if (this.list[e.id].num == 0) {
this.list[e.id].num = 0;
} else {
this.list[e.id].num--;
}
}
}
c.删除商品功能
子组件中绑定点击事件
<span @click="del(index)">×</span>
子组件定义方法,声明自定义事件
del(i) {
this.$emit('cart-del', i);
}
父组件绑定自定义事件
<cart-list :list="list" @cart-del="del($event)" @goods-num="changeNum($event)"></cart-list>
父组件中定义方法:
del(i) {
this.list.splice(i, 1);
}
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
input {
outline: none;
width: 30px;
height: 30px;
margin: 0 5px;
}
button {
width: 20px;
font-size: 20px;
}
#cart {
width: 500px;
margin: 50px auto;
}
.title {
background-color: #A2C8D5;
text-align: center;
height: 50px;
line-height: 50px;
}
li {
font-size: 20px;
height: 70px;
line-height: 70px;
background-color: #E9E9E9;
padding: 0 10px;
border-bottom: 2px solid #C5CDCB;
}
li span:first-of-type {
float: left;
}
li span:last-of-type {
float: right;
margin-left: 20px;
color: #D61615;
font-size: 60px;
cursor: pointer;
width: 70px;
text-align: center;
}
li span:last-of-type:hover {
background-color: #f7b709;
}
li .btn {
float: right;
}
.footer {
height: 70px;
background-color: #E8C04A;
line-height: 70px;
text-align: center;
position: relative;
}
.footer>span:first-child {
color: red;
font-size: 25px;
}
.footer>span:last-child {
position: absolute;
top: 50%;
right: 40px;
transform: translateY(-50%);
width: 80px;
height: 40px;
background-color: #CC4F45;
line-height: 40px;
}
</style>
</head>
<body>
<div id="app">
<my-cart></my-cart>
</div>
<script src="./vue.min.js"></script>
<script>
// 购物车标题子组件
let cartTitle = {
props: ['uname'],
template: ` <div class="title">{{uname}}的商品</div>`
}
// 购物车商品列表子组件
let cartList = {
props: ['list'],
template: `<ul>
<li :key="item.id" v-for="(item,index) in list"><span>{{item.name+' 价格: '+item.price}}</span><span @click="del(index)">×</span>
<div class="btn"><button @click="sub(index)">-</button><input type="text" :value="item.num" @blur="changeNum(index,$event)"><button @click="add(index)">+</button></div>
</li>
</ul>`,
methods: {
del(i) {
this.$emit('cart-del', i);
},
changeNum(i, e) {
this.$emit('goods-num', {
id: i,
num: e.target.value,
type: 'inputChange'
})
},
sub(i) {
this.$emit('goods-num', {
id: i,
type: 'subChange'
})
},
add(i) {
this.$emit('goods-num', {
id: i,
type: 'addChange'
})
}
}
}
// 购物车总价子组件
let cartTotal = {
props: ['list'],
template: ` <div class="footer">
<span>总价:<span>{{sum}}</span></span>
<span>结算</span>
</div>`,
computed: {
sum() {
let sum = this.list.reduce((total, e) => {
return total + e.price * e.num;
}, 0)
return sum;
}
}
}
// 购物车父组件
Vue.component('my-cart', {
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: '手机',
price: 6000,
num: 1
}, {
id: 2,
name: '电脑',
price: 8000,
num: 1
}, {
id: 3,
name: '耳机',
price: 200,
num: 5
}, {
id: 4,
name: '充电宝',
price: 100,
num: 10
}]
}
},
template: `<div id="cart">
<cart-title :uname="uname"></cart-title>
<cart-list :list="list" @cart-del="del($event)" @goods-num="changeNum($event)"></cart-list>
<cart-total :list="list"></cart-total>
</div>`,
components: {
'cart-title': cartTitle,
'cart-list': cartList,
'cart-total': cartTotal
},
methods: {
del(i) {
this.list.splice(i, 1);
},
changeNum(e) {
if (e.type == 'inputChange') {
this.list[e.id].num = e.num;
} else if (e.type == 'addChange') {
this.list[e.id].num++;
} else {
if (this.list[e.id].num == 0) {
this.list[e.id].num = 0;
} else {
this.list[e.id].num--;
}
}
}
}
})
let vu = new Vue({
el: '#app',
data: {}
})
</script>
</body>
</html>
更多推荐
所有评论(0)