稀土掘金 稀土掘金

Vue.js之简单购物车的实现

​本文已参与「新人创作礼」活动,一起开启掘金创作之路

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

\

玻璃钢生产厂家南部玻璃钢花盆花器云南玻璃钢造型景观雕塑制作毕节商场美陈制作达州玻璃钢广场雕塑定制玻璃钢马雕塑设计哪里有玻璃钢龙雕塑设计方案韶关玻璃钢雕塑摆件亳州公园玻璃钢雕塑多少钱知名玻璃钢雕塑服务至上玻璃钢雕塑 修复卡通企鹅雕塑户外玻璃钢济源室外玻璃钢雕塑公司重庆泰安玻璃钢花盆厂家商场的美陈都是统一采购的吗富国岛游乐场玻璃钢雕塑图片江苏玻璃钢仿真水果雕塑价格平顶山肖像玻璃钢景观雕塑公司浙江特色商场美陈有哪些南昌玻璃钢雕塑供应商安宁玻璃钢雕塑加工厂贵不贵重庆创意玻璃钢雕塑美陈玻璃钢卡通雕塑参考价南京清远玻璃钢动物雕塑白城玻璃钢雕塑工厂大玻璃钢花盆哪家买山东仿铜玻璃钢雕塑订做价格玻璃钢鹿雕塑订制连云港玻璃钢仿真水果雕塑陵水黎族自治县玻璃钢雕塑常州玻璃钢雕塑公司香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化