|
```html 购物车页面,个购本质上是物车一个状态机。用户点加号,页面数量变,个购点删除,物车列表变,页面勾选复选框,个购总价变。物车这些变化如果靠手动操作DOM,页面代码会迅速膨胀成一团乱麻。个购Vue的物车响应式系统正好掐住了这个痛点——你只管改数据,页面自己会跟着动。页面 先搭个最基础的个购架子。商品数据放数组里,物车每个对象带上id、页面名称、单价和数量。用v-for渲染列表,每一行绑一个复选框,底部固定一个结算栏。computed属性里算两件事:全选状态和总金额。注意这里有个细节——全选不能简单用every()返回布尔值,因为当列表为空时,every()会返回true,这显然不对。得加个长度判断。 <div v-for="item in cartList" :key="item.id"> <input type="checkbox" v-model="item.checked"> <span>{ { item.name }}</span> <span>¥{ { item.price * item.count }}</span> <button @click="changeCount(item, -1)">-</button> <span>{ { item.count }}</span> <button @click="changeCount(item, 1)">+</button> <button @click="removeItem(item.id)">删除</button> </div> 数量增减的函数写起来要小心。减到0的时候,是直接清零还是弹窗确认?我倾向于直接置为0,但保留商品在列表里,让用户自己删。这个交互逻辑得跟产品经理对齐,不同场景有不同答案。不过有个通用原则——不要做超出预期的动作,用户点减号,就只减数量,别顺手把商品移出列表,那会让人困惑。 总价的计算有个坑。如果用户在数量框里手动输入了非法值,比如负数或者小数,computed会算出个奇怪的结果。解决办法是在上加type="number"和min="1",但这只能防君子不防小人。更稳妥的做法是在计算总价前先过滤一遍数据,把无效数量强制转成1。这种数据清洗的逻辑放在computed里虽然可行,但会显得臃肿。我习惯把清洗动作放在事件处理函数里,保持computed只做纯计算。 全选按钮的实现,别用事件去逐个设置checked属性。利用computed的getter和setter更优雅——getter返回所有项是否都被勾选,setter里统一赋值。这样UI和逻辑完全解耦,点全选时,Vue会自动把新值写进每一项的checked里。代码量少一半,还不会出现漏改某个子项的bug。 computed: { isAllChecked: { get() { return this.cartList.length > 0 && this.cartList.every(item => item.checked) }, set(val) { this.cartList.forEach(item => item.checked = val) } }, totalPrice() { return this.cartList .filter(item => item.checked) .reduce((sum, item) => sum + item.price * item.count, 0) } } 删除功能有个隐藏需求——批量删除。用户勾选好几项后,点一个“删除选中”按钮,比一个个点叉快得多。这个按钮的实现逻辑很简单,用filter生成新数组替换旧数组就行。但要注意,Vue的响应式系统对数组索引的变更检测有局限,直接改cartList[0]不会触发更新,得用splice或整体重新赋值。新手常在这栽跟头,我见过有人用Vue.set去改数组索引,其实完全没必要。 页面写完,还得考虑状态持久化。用户刷新页面,购物车就空了,这体验很差。配合localStorage,在watch里监听cartList的深度变化,每次变更后把JSON字符串存进去。初始化时从localStorage读取,没有就用空数组。这个方案简单粗暴,但够用。要注意的是,存储时得过滤掉函数和循环引用,普通商品数据没这个问题。 样式上,购物车页面最容易犯的错是布局混乱。商品列表和结算栏之间要有清晰的视觉分隔,结算栏固定底部,用position: sticky配合bottom: 0就能实现。别用fixed,那会把结算栏盖在键盘上,在手机上尤其难受。另外,复选框的尺寸别太小,拇指够不着,至少44像素。 最后说个性能细节。如果商品列表超过100项,v-for里嵌套的按钮事件绑定会有点吃紧。但购物车一般不会这么长,真到了那个规模,得考虑虚拟滚动。不过那是另一个话题了,现阶段用Vue的响应式特性,把数据流理顺,购物车页面就已经能跑得飞快。 ``` |