【饿了么】—— Vue2.0高仿饿了么核心模块&移动端Web App项目爬坑(二)
前言:上一篇项目总结介绍了页面骨架的开发、header组件的开发,这一篇主要梳理:商品组件开发、商品详情页实现。项目github地址:https://github.com/66Web/ljq_eleme,欢迎Star。
![]() |
![]() |
goods |
一、商品组件开发 |
- App.vue主组件传seller对象给每个路由:
<router-view :seller="seller"></router-view>
两栏布局-flex布局手机屏幕自适应
- 设计:无高度滚动条,高度超过视口高度就会隐藏
<div class="goods">
<div class="menu-wrapper"></div>
<div class="foods-wrapper"></div>
</div>
.goods
display: flex
position: absolute
top: 174px
bottom: 46px
width: 100%
overflow: hidden
.menu-wrapper
flex: 0 0 80px
width: 80px
background: #f3f5f7
.foods-wrapper
flex: 1
左侧布局-菜单列表
- 需求:文字标题可能单行,也可能多行,但都要在列表项中垂直居中
- 小技巧:使用列表项display:table,文字标题disable:table-cell
.menu-item
display: table
height: 54px
width: 56px
line-height: 14px
.text
display: table-cell
width: 56px
vertical-align: middle
font-size: 12px
- 关于box-sizing:border-box; 规定两个并排的带边框的框
右侧布局-食品列表
- 列表嵌套:第一层遍历商品项item in goods, 第二层遍历单个商品的信息项food in item.foods
列表滚动-better-scroll 第三方JS库
- 基于iscroll重写的库:better-scroll详解博客【重点突破】—— 当better-scroll 遇见Vue
- 安装:
cnpm install better-scroll --save
- 使用步骤:
- goods.vue 引入:
import BScroll from 'better-scroll';
- ref 属性获取dom元素:驼峰命名法
<div class="menu-wrapper" ref="menuWrapper"> <div class="foods-wrapper" ref="foodsWrapper">
-
better-scroll初始化:
methods: { _initScroll(){ this.meunScroll=new BScroll(this.$refs.menuWrapper,{}); this.foodsScroll=new BScroll(this.$refs.foodsWrapper,{}); } }
-
成功回调函数中调用_initScroll方法:
this.$nextTick(()=>{ this._initScroll(); })
-
this.$nextTick()这个方法作用是当数据被修改后使用这个方法会回调获取异步更新后的dom再render出来
-
如果不在下面的this.$nextTick()方法里回调这个方法,数据改变后再来计算滚动轴就会出错
左右联动
- 需求:滚动右侧,左侧跟着变化;点击左侧,右侧滚动到相应位置
- 原理:依赖右侧滚动列表实时变化的Y值(纵坐标),移动到哪个区间,左侧列表就要显示哪个区间
- 【滚动右侧时左侧相应滚动】思路&实现:
- 在data中定义数组用来存储不同区间的高度
data () { return { goods:[], listHeight: [] } }
-
为了获取高度,给food-list定义一个class--food-list-hook,不用来编写css,专门用来获取DOM元素,没有实际的效果,只是用来被js选择的
<li v-for="item in goods" :key="item.id" class="food-list food-list-hook">
-
定义foodList拿到每个li,每个li是包括包括标题在内的每一类food的高度,不是单独的一种good,将_calculateHeight放在nextTick中初始化_initScroll的后面,保证其能正确计算到高度
_calculateHeight() { //food-list-hook类的添加知识为了能拿到food列表,例如,拿到的是多个类似整个粥品的区块 let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook'); let height = 0; this.listHeight.push(height); //listHeight是一个递增的区间数组,是每个专区高度的累加 for (let i = 0; i < foodList.length; i++) { let item = foodList[i]; height += item.clientHeight; this.listHeight.push(height); } }
-
在data中定义一个scrollY对象,用来跟踪滚动的高度 scrollY:0;在初始化betterScroll时,为右侧添加probeType--可以检测到右侧实时滚动的位置,监听scroll,将其实时滚动的位置暴露出来
data () { return { goods:[], listHeight: [], scrollY: 0 } }
_initScroll() { this.meunScroll=new BScroll(this.$refs.menuWrapper,{ click: true //使better-scroll可点击,默认派发一个点击事件 }); this.foodsScroll=new BScroll(this.$refs.foodsWrapper,{ click: true, probeType: 3 //BScroll滚动时,能实时告诉我们滚动的位置,类似探针的效果 }); //foodsScroll监听事件,在scroll滚动时能见位置实时暴露出来 this.foodsScroll.on('scroll', (pos) => { this.scrollY = Math.abs(Math.round(pos.y));//本身是个负值,取正值 }) }
-
拿到滚动的高度和内容区的固定高度之后, 查看scrollY落在哪个区间,并返回那个区间的索引(!height2是测试最后一个区间的)其中,>= 向下的是一个闭区间,这样第一个就会高亮了
computed: { currentIndex() { //currentIndex对应菜单栏的下标 for (let i = 0; i < this.listHeight.length; i++) { //不要忘了加this引用 let height1 = this.listHeight[i]; let height2 = this.listHeight[i + 1]; //获得了一个区间的上下范围,判断scrollY落到这个区间,!height2是判断最后一个区间 //避免i溢出,>= 向下的是一个闭区间,这样第一个就会高亮了 if (!height2 || (this.scrollY >= height1 && this.scrollY < height2)) { return i; //映射到第5行menu的变化 } } return 0; }
-
拿到index之后,回到左侧的menu区,当我们遍历menu的时候,如果$index等于我们计算得到的currentIndex时,就为当前的li添加一个current样式
<!-- 如果index等于currentIndex,就为这个li添加一个current类,改变左侧导航栏的背景颜色--> <li v-for="(item,index) in goods" :key="item.id"
class="menu-item" :class