Vue(小案例_vue+axios仿手机app)_购物车(二模拟淘宝购物车页面,点击加减做出相应变化)
一、前言
在上篇购物车中,如果用户刷新了当前的页面,底部导航中的数据又会恢复为原来的:
1、解决刷新,购物车上数值不变
2、在购物车点击加减按钮,数值做出对应变化
二、主要内容
1、实现效果:点击购物车的时候可以查看到购物车的商品信息
2、解决刷新,购物车上数值不变
(1)新建一个Cart.vue展示购物信息,并将路由添加到index.js中
(2)购物车里面涉及到:存储商品信息,获取商品信息,保存商品信息,这里将这些方法单独封装到一个.js文件中(本来需要在后台操作)
let obj={}; //定义一个对象,存储商品信息 //这里需要存储数据 //{商品的id, 商品的数量} //保存商品 obj.saveGoods = function(goodsList){ window.localStorage.setItem('goodsList',JSON.stringify(goodsList)) } //获取商品的值 obj.getGoodsList = function(){ return JSON.parse(window.localStorage.getItem('goodsList'|| '{}'))//如果stroge中没有值,返回一个空对象 } //增加商品 obj.add = function(goods){ let goodsList = this.getGoodsList()//获取到storage里面的对象 if(goodsList[goods.id]){ //goods.id是商品的数量,对应有值的话就追加 goodsList[goods.id] = goodsList[goods.id] + goods.num; }else{ goodsList[goods.id]=goods.num; } //传进来之后还需要保存 this.saveGoods(goodsList); } //获取购物车的总数量 obj.getTotalCount = function(){ let goodsList = this.getGoodsList(); let values = Object.values(goodsList);//Object.values返回的是一个数组,里面对应着每一个key的value let sum = 0; values.forEach(val => sum = sum +val); return sum; } export default obj;
(2)封装好了方法就要在商品详情页面使用(类似于我们在淘宝上买东西,当你在商品详情页面点击加入购物车之后,购物车里面会有对应的商品)
name:'GoodsDetail', data(){ return{ url:`getthumImages/${this.$route.params.id}`, goodsInfo:{},//当前购物车的信息,里面有id pickNum:1 , isExist:false //让小球默认是隐藏的状态, } }, methods:{ afterEnter(){ this.isExist=false; //显示出来之后执行这个,又将小球隐藏 this.$bus.$emit('sendPickNum',this.pickNum); //当触发了上面的事件之后, GoodsTool.add({ id:this.goodsInfo.id, num:this.pickNum }) }, //点击加入购物车执行这个方法,然后让小球显示出来 ballHandler(){ this.isExist=true; // this.$bus.$emit('sendPickNum',this.pickNum); //将当前的pickNum传过去,但是这个不能加在这里,否者一点击“加入购物车就传 },
(3)当你一刷新,stroge里面的数据清空,下面的购物车上面的数值又变成0了,所以在下面的底部导航里,让他一开始就获取到当前这个总数
created(){ //当你的组件一创建好了后就挂载这个bus公交车,$on这个事件监听 this.$bus.$on(`sendPickNum`, (data)=>{ this.pickNum=this.pickNum + data; / }), this.pickNum=GoodsTool.getTotalCount();//直接将这Goods.vue里面传来的数据赋值, }
(4)通过以上思路,就能保证刷新页面时,购物车中的数据不发生改变
3、点击底部导航“购物车”,将上面加进的东西和数量展示在页面中,(在购物车点击加减按钮,数值做出对应变化)
(1)首先发送请求,这里发送请求的时候,需要之后再购物详情页面加入的商品的id, 然后根据对应的id拿到对应的请求数据
(2)拿到购物详情里面的id
let goodsList = GoodsTool.getGoodsList();//["88":5,"99":4] 第一个数商品id 第二个是商品数量 let ids = Object.key(goodsList).join(',');//Object.key()获取key值 [88,99]
(3)拿到上面的id之后就可以发请求
created(){ let goodsList = GoodsTool.getGoodsList();//{"88":5,"99":4} 第一个数商品id 第二个是商品数量 let ids = Object.key(goodsList).join(',');//Object.key()获取key值 [88,99] if(ids){ this.$axios.get(`goods/getshopcarlist${ids}`) .then(res=>{ this.shopCart=res.data.message; } }) }
(4)但是这里的后端数据库里并没有保存加入购物车的数量(如果有可以直接从后端获取),这里手动添加
created(){ let goodsList = GoodsTool.getGoodsList();//{"88":5,"99":4} 第一个数商品id 第二个是商品数量 let ids = Object.key(goodsList).join(',');//Object.key()获取key值 [88,99] if(ids){ this.$axios.get(`goods/getshopcarlist${ids}`) .then(res=>{ this.shopCart=res.data.message;//返回的是一个数组 //给数组元素添加属性 for(var i=0; i<this.shopCart.length;i++){ let shop=this.shopCart[i];//获取到当前的对象 let num = goodsList[shop.id];//根据当前对象的id查找到对应的购物车数量 shop.num = num;//给每个对象添加一个num属性,并在视图上渲染,但是会发现视图上的值并没有改变 } } }) }
(5)在购物车列表做加减操作,并且将当前的对象传进去
<li class="p-list" v-for="(shop, index) in shopCart"> <mt-switch></mt-switch> <img src=""> <div class="pay-calc"> <p>{{shop.title}}</p> <div class="calc"> <span>¥777</span> <span @click="substract(shop)">-</span> <span>{{shop.num}}</span> <span @click="addNum(shop)">+</span> <a href="javascript:;">删除</a> </div> </div> </li>
methods:{ addNum(shop){//每次点击都接受到当前的对象 shop.num++; //这里的值虽然加上了,但是,数据并没有响应上去,是因为created是一开始就加载的,后来点击修改了num的值,但是没有 响应视图 console.log(shop) }, substract(shop){ if(shop.num==1){ return; } shop.num--; } },
(6)做完第五步,在测试的时候,发现控制台中打印出来的对象中num改变,但是视图上并没有改变
原因:vue 中存在一种机制会将shopCart(也就是你请求的数据进行监视)属性进行监视,完成响应式(因为后面的num是我们自己加进去的,没有挂载到数据属性里面,vue就会对这个属性进行监视,监视到数据不是完全挂载到数据属性上的,就无法完成响应操作)
在vue中的源码中会生成 Object.defineProperty(this, 'shopCart', { set:function(){ //判断shopCart元素是否有属性 })
解决方法:如果数据不完整挂载的情况在要添加属性,就需要手动通知vue完成响应式,==》双向数据绑定
created(){ let goodsList = GoodsTool.getGoodsList();//{"88":5,"99":4} 第一个数商品id 第二个是商品数量 let ids = Object.key(goodsList).join(',');//Object.key()获取key值 [88,99] if(ids){ this.$axios.get(`goods/getshopcarlist${ids}`) .then(res=>{ this.shopCart=res.data.message;//返回的是一个数组 //给数组元素添加属性 for(var i=0; i<this.shopCart.length;i++){ let shop=this.shopCart[i];//获取到当前的对象 let num = goodsList[shop.id];//根据当前对象的id查找到对应的购物车数量 if(num){ shop.num = num; this.$set(shop, 'num', num);//自己给这个数据进行双向数据绑定 this.$set(shop,'isSelected',true); } } }) }
三、总结
踩坑一:自己收动添加的属性,需要用$set()给这个属性绑定
踩坑二:vue 中存在一种机制会将shopCart(也就是你请求的数据进行监视)属性进行监视,完成响应式(因为后面的num是我们自己加进去的,没有挂载到数据属性里面,vue就会对这个属性进行监视,监视到数据不是完全挂载到数据属性上的,就无法完成响应操作)