实战:开发购物车

在开始写代码前,要对需求进行分析,这样有助于理清业务逻辑,尽可能还原设计与产品交互。

购物车需要展示一个已加入购物车的商品列表,包括商品名称、商品单价、购买数量和操作等信息,还需要实时显示购买的总价。其中购买数量可以增加或减少,每类商品还可以从购物车中移除。

这里需要把js文件写在<body>的最底部,如果写在<head>里面,Vue实例将无法创建,因为此时DOM还没有被解析完成,除非通过异步或在事件DOMContentLoaded(IE是onreadystatechange)触发时再创建Vue实例,这有点像jQuery的$(document).ready()方法。

在实际业务中,数据列表应该是通过AJAX从服务端动态获取,这里直接写入在data选项内,另外每个商品还应该有一个全局唯一的ID

data: {
    list: [
        {
            id: 1,
            name: 'iPhone 7',
            price: 6188,
            count: 1
        }, {
            id: 2,
            name: 'iPad Pro',
            price: 5888,
            count: 1
        }
    ]
}

因为每个商品都是可以从购物车移除的,所以当列表为空时,在页面中显示一个“购物车为空”的提示更为友好,可以通过判断数组list的长度来实现该功能。

<div id="app" v-cloak>
    <template v-if="list.length"></template>
    <div v-else>购物车为空</div>
</div>

<template>中的代码分两部分,一部分是商品列表信息,用表格table展示;另一部分是带有千分位分隔符的商品总价(每个三位数加进一个逗号)。

<template v-if="list.length">
    <table>
        <thead>
        <tr>
            <th></th>
            <th>商品名称</th>
            ...
        </tr>
        </thead>
        <tbody></tbody>
    </table>
    <div>总价:¥ {{totalPrice}}</div>
</template>    

总价totalPrice是依赖于商品列表动态变化的,所以用计算属性来实现。

computed: {
    totalPrice: function () {
        var total = 0;
        for (var i = 0; i < this.list.length; i++) {
            var item = this.list[i];
            total += item.price * item.count;
        }
        return total.toString().replace(/\B(?=(\d{3})+$)/g, ',');
    }
}

商品序号、名称、单价、数量都是直接使用插值完成。

很多时候,一个元素上会同时使用多个特性(尤其是在组件中使用props传递数据时),写在一行代码较长,不便阅读,所以建议特性过多时,将每个特性都单独写成一行。比如<button>中使用了v-bindv-on两个指令。

每件商品购买数量最少是1件,所以当count为1时,不允许再继续减少,这里给<button>动态绑定了disabled特性来禁用按钮。

<tbody>
<tr v-for="(item,index) in list">
    <td>{{index+1}}</td>
    <td>{{item.name}}</td>
    <td>{{item.price}}</td>
    <td>
        <button @click="handleReduce(index)" :disabled="item.count===1">-</button>
        {{item.count}}
        <button @click="handleAdd(index)">+</button>
    </td>
    <td>
        <button @click="handleRemove(index)">移除</button>
    </td>
</tr>
</tbody>