vue饿了么项目-goods商品列表页开发
Posted 乘客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue饿了么项目-goods商品列表页开发相关的知识,希望对你有一定的参考价值。
1.flex 属性是 flex-grow、flex-shrink 和 flex-basis 属性的简写属性。
flex-grow | 一个数字,规定项目将相对于其他灵活的项目进行扩展的量。 |
flex-shrink | 一个数字,规定项目将相对于其他灵活的项目进行收缩的量。 |
flex-basis | 项目的长度。合法值:"auto"、"inherit" 或一个后跟 "%"、"px"、"em" 或任何其他长度单位的数字。 |
2.采用绝对定位,相对于父元素
.good
display flex
position absolute
width 100%
top 174px
bottom 46px
overflow hidden
3.使用vue-resourse获取json并应用到模板
现在越来越多的数据传输方式都是json数据格式,包括用jquery开发时,也有很好用的$.ajax来进行数据请求与处理,那么vue-resource提供了一种类似的,并且api更加简洁易用,压缩后文件更小。配合ES 6的Lambda写法,更加优雅
官网:https://github.com/pagekit/vue-resource/blob/master/docs/http.md
props: { seller: { type: Object } }, data () { return { goods: [], //一开始goods为空 listHeight: [], scrolly: 0, selectedFood: {} }; }, created() { //当这个组件被调用的时候,通过后端获得数据赋值给goods this.$http.get(‘/api/goods‘).then((response) => { // ‘/api/goods‘请求的是data.json下的goods数组 response = response.body; if (response.errno === ERR_OK) { this.goods = response.data; this.$nextTick(() => { //可以用$nextTick
來确保Dom变化后
再执行一些事情 this._initScroll(); this._calculateHeight(); }); } });this.classMap = [‘decrease‘, ‘discount‘, ‘special‘, ‘invoice‘, ‘guarantee‘]; },
注:vue更新到2.0之后,作者就宣告不再对vue-resource更新,而是推荐的axios,它的基本用法可以参考:http://www.kancloud.cn/yunye/axios/234845
4.遍历取数据
<span class="text"> <span v-show="item.type>0" class=" icon" :class="classMap[item.type]"></span>{{item.name}} </span>
classMap[item.type]是一个数组,通过item.type去取对应的class,item.type是data.json中mock的数据
5.display table
此元素会作为块级表格来显示(类似 <table>),表格前后带有换行符。
在table中可用vertical-align middle实现垂直居中
6.添加better-scroll依赖
链接:https://github.com/ustbhuangyi/better-scroll
<div class="menu-wrapper" ref="menuWrapper"> <ul> <li v-for="(item, index) in goods" class="menu-item border-1px" :class="{‘current‘:currentIndex === index}" @click="selectMenu(index, $event)"> <span class="text"> <span v-show="item.type>0" class=" icon" :class="classMap[item.type]"></span>{{item.name}} </span> </li> </ul> </div> <div class="foods-wrapper" ref="foodWrapper"> <ul> <li v-for="item in goods" class="food-list food-list-hook"> <h1 class="title">{{item.name}}</h1> <ul> <li v-for="food in item.foods" class="food-item" @click="selectFood(food, $event)"> <div class="icon"> <img :src="food.icon" alt="" width="57"> </div> <div class="content"> <h2 class="name">{{food.name}}</h2> <p class="desc">{{food.description}}</p> <div class="extra"> <span class="count">月售{{food.sellCount}}</span><span class="count">好评{{food.rating}}</span> </div> <div class="price"> <span class="now">¥{{food.price}}</span><span class="old" v-show="food.oldPrice">¥{{food.oldPrice}}</span> </div> <div class="cartControl-wrapper"> <cartControl :food="food" @increment="incrementTotal"></cartControl> </div> </div> </li> </ul> </li> </ul> </div>
6.1 $refs
的使用是vue 2 操作dom的一种方式
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子组件上,引用就指向组件实例:
_initScroll(){ //初始化scroll区域 this.menuScroll = new BScroll(this.$refs.menuWrapper, { click: true //结合BScroll的接口使用,是否将click事件传递,默认被拦截了 }); this.foodsScroll = new BScroll(this.$refs.foodsWrapper, { probeType: 3 //结合BScroll的接口使用,3实时派发scroll事件,探针的作用 }); //结合BScroll的接口使用,监听scroll事件(实时派发的),并获取鼠标坐标,当滚动时能实时暴露出scroll this.foodsScroll.on(‘scroll‘, (pos) => { //事件的回调函数 this.scrollY = Math.abs(Math.round(pos.y));//滚动坐标会出现负的,并且是小数,所以需要处理一下,实时取得scrollY
}) }
vue中更改数据,DOM会跟着做映射,但vue更新DOM是异步的,用 $nextTick
()来确保Dom变化后能调用到_initScroll()方法。调用_initScroll()方法能计算内层ul的高度,当内层ul的高度大于外层wrapper的高度时,可以实现滚动。
6.2 左右两边联动
-
在vue实例生命周期的开始created分别加载
_initScroll
和_calculateHeight
-
通过
_calculateHeight
计算foods内部每一个块的高度,组成一个数组listHeight -
在_initScroll里面,设置了bscroll插件的一个监听事件scroll,将food区域当前的滚动到的位置的y坐标设置到一个vue实例属性scrollY
this.scrollY = Math.abs(Math.round(pos.y));
-
通过计算属性currentIndex,获取到food滚动区域对应的menu区域的子块的索引,然后通过设置一个class来做样式切换变化
:class="{‘current‘:currentIndex === index}
,实现联动 -
另外当点击menu 区域的时候,会触发selectMenu事件,也会根据点击到的menu子块的索引然后去触发food区域滚动到对应的高度区块区间
this.foodsScroll.scrollToElement(el, 300);scrollToElement():是better-scroll中的方法,滚动到某个元素,el(必填)表示 dom 元素,time 表示动画时间,offsetX 和 offsetY 表示坐标偏移量,easing 表示缓动函数
-
这样完成整个对应
_calculateHeight()方法计算各个右侧区间的高度
_calculateHeight(){ let foodList = this.$refs.foodsWrapper.getElementsByClassName(‘food-list-hook‘); //获取每一个food的dom对象 let height = 0; this.listHeight.push(height); //初始化第一个高度为0 for (let i = 0; i < foodList.length; i++) { let item = foodList[i]; //每一个item都是刚才获取的food的每一个dom height += item.clientHeight; //主要是为了获取每一个foods内部块的高度 this.listHeight.push(height); } } }
实时取得scrollY的值后,需要与左边进行映射,利用计算属性:
computed: { currentIndex(){ //计算到达哪个区域的区间的时候的对应的索引值 for (let i = 0; i < this.listHeight.length; i++) { let height1 = this.listHeight[i]; //当前menu子块的高度 let height2 = this.listHeight[i + 1]; //下一个menu子块的高度 //滚动到底部的时候,height2为undefined,需要考虑这种情况 //需要确定是在两个menu子块的高度区间 if (!height2 || (this.scrollY >= height1 && this.scrollY < height2)) { return i; //返回这个menu子块的索引 } } return 0; }, selectFoods() { //自动将所有的goods.food添加一个count属性,方便做数量运算 let foods = []; this.goods.forEach((good) => { good.foods.forEach((food) => { if (food.count) { foods.push(food); } }); }); return foods; } }
:class="{‘current‘:currentIndex === index}"当currentIndex === index时才设置current这个class
点击左侧 ,右侧响应:
关于在selectMenu中点击,在pc界面会出现两次事件,在移动端就只出现一次事件的问题:
原因:bsScrooler会监听事件(例如touchmove,click之类),并且阻止默认事件(prevent stop),并且他只会监听移动端的,pc端的没有监听
在pc页面上 bsScroller也派发了一次click事件,原生也派发了一次click事件
//bsScroll的事件,有_constructed: true MouseEvent {isTrusted: false, _constructed: true, screenX: 0, screenY: 0, clientX: 0…} //pc的事件 MouseEvent {isTrusted: true, screenX: -1867, screenY: 520, clientX: 53, clientY: 400…}
解决:针对bsScroole的事件,有_constructed: true,所以做处理,return掉非bsScroll的事件
selectMenu(index, event){ if (!event._constructed) { //去掉自带的click事件点击,即pc端直接返回 return; } let foodsList = this.$refs.foodsWrapper.getElementsByClassName(‘food-list-hook‘); let el = foodsList[index]; //类似jump to的功能,通过这个方法,跳转到指定的dom this.foodsScroll.scrollToElement(el, 300); },
7.shopcart组件
也是采用flex布局,右侧部分固定宽度(flex 0 0 105px),左边自适应宽度(flex 1)
采用固定定位,定位在底部(position fixed)
横向排列display:inline-block
包含购物车图标的div超出了父元素的高度,我们使用position:relative,并设置top为负来实现
box-sizing: border-box; 则div 设置的宽高将包含 边框及 padding
border-radius 50%,形成一个圆
选择了多少商品:定义成数组,底栏其余部分的变化都基于这个对象的变化而变化
selectFoods: { type: Array, default() { return [{price: 20, count: 2}]; } }
计算部分(都基于selectFoods进行相应计算)computed中的函数可以直接在Tempplate中以指针的形式引用
computed: { totalPrice() {//计算总价,超过起送额度后提示可付款 let total = 0; this.selectFoods.forEach((food) => { total += food.price * food.count; }); return total; }, totalCount() {//计算选中的food数量,在购物车图标处显示,采用绝对定位,top:0;right:0;显示在购物车图标右上角 let count = 0; this.selectFoods.forEach((food) => { count += food.count; }); return count; }
控制底部右边内容随food的变化而变化,payDesc()控制显示内容,payClass()添加类调整显示样式
在template中 <div class="pay" :class="payClass"> {{payDesc}} </div>
在computed中: payDesc() { if (this.totalPrice === 0) { return `¥${this.minPrice}元起送`; //这里使用的是es6中的反引号 } else if (this.totalPrice < this.minPrice) { let diff = this.minPrice - this.totalPrice; return `还差¥${diff}元起送`; } else { return ‘去结算‘; //单引号,单引号和反引号不同 } }, payClass() { if (this.totalPrice < this.minPrice) { return ‘not-enough‘; } else { return ‘enough‘; } }
总结:通过以上学习我们能发现,selectFoods()的变化起着关键作用,它的变化会引起DOM的变化,并最终体现到界面上,而我们不用关注DOM内部的具体实现,这就是vue的一大好处。如果采用jQuery完成这些功能会略显繁杂。
8 cartcontrol组件,它是shopcart的子组件
可以给按钮增加padding,方便用户点击
this.foodScroll = new BScroll(this.$refs.foodWrapper, { probeType: 3, click: true });
click: true
是否派发click事件
通过import Vue from ‘vue‘;使用set接口,通过vue.set()添加属性,当它变化时就能被检测到
methods: { addCart(event) { if (!event._constructed) { // 去掉自带click事件的点击 return; } if (!this.food.count) { Vue.set(this.food, ‘count‘, 1); } else { this.food.count++; } // event.srcElement.outerhtml this.$emit(‘increment‘, event.target); // 子组件通过 $emit触发父组件的方法 increment 还 }, decreaseCart(event) { if (!event._constructed) { // 去掉自带click事件的点击 return; } this.food.count--; } } };
9 为减号按钮添加平移、滚动的动画
<transition name="fade"> //减号和数字平移动画 <div class="cart-decrease" v-show="food.count>0" @click.stop.prevent="decreaseCart($event)"> <transition name="inner"> //数字滚动动画 <span class="inner iconfont icon-jian"></span> </transition> </div> </transition>
&.fade-enter-active, &.fade-leave-active { transition: all 0.4s linear <--过渡效果的 CSS 属性的名称、过渡效果需要多少时间、速度效果的速度曲线--> } &.fade-enter, &.fade-leave-active { opacity: 0 transform translate3d(24px, 0, 0) //这样可以开启硬件加速,动画更流畅,3D旋转,X轴位移24px } .inner display inline-block <--设置成inline-block才有高度,才能有动画--> line-height 24px font-size 24px vertical-align top color rgb(0, 160, 220, 0.2) &.inner-enter-active, &.inner-leave-active { transition: all 0.4s linear transform: rotate(0) } &.inner-enter, &.inner-leave-active { opacity: 0 transform rotate(180deg) }
10 购物小球(抛物线小球)
以上是关于vue饿了么项目-goods商品列表页开发的主要内容,如果未能解决你的问题,请参考以下文章