目录

1.静态布局

2.组件划分和封装

(1)组件划分

(2)组件封装

3.功能实现

(1)标题组件

(2)总价组件

(3)商品列表组件

4.总结


本文主要介绍如何利用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>

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐