Vue框架之goods组件开发详解
一、布局Flex
Flex布局,可以简便、完整、响应式地实现各种页面布局,Flex是FlexibleBox的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为Flex布局。
//指定为Flex布局
display:flex;
//主要属性
flex:none|[<'flex-grow'><'flex-shrink'>?||<'flex-basis'>]
flex属性是flex-grow,flex-shrink和flex-basis的简写,默认值为01auto。后两个属性可选。
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
flex-shrink属性定义项目的缩小比例,默认为1,即如果空间不足,该项目将缩小,flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小
flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(mainsize)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小,设为跟width或height属性一样的值(比如350px),则项目将占据固定空间
flex:等分内容缩放展位空间;
flex:0080px
二、图标组件
子组件iconMap
exportdefault{
props:{//图标类型
iconType:Number
},
created(){//数组类名
this.iconClassMap=['decrease','discount','special','invoice','guarantee']
}
}
父组件goods
importiconMapfrom'../iconMap/iconMap'//注意路径写法
//注册组件
components:{
iconMap
}
//json数据根据type判断是否有图标
0":iconType="item.type">
{{item.name}}
三、better-scroll应用
类似iscroll实现滚动效果
安装
npminstallbetter-scroll
引入
importBScrollfrom'better-scroll'
说明
(1)原理:父容器wrapper,它具有固定的高度,当它的第一个子元素content的高度超出了wrapper的高度,我们就可以滚动内容区了,若没有超出则不能滚动了。
(2)better-scroll的初始化
better-scroll的初始化时机很重要,因为它在初始化的时候,会计算父元素和子元素的高度和宽度,来决定是否可以纵向和横向滚动。因此,我们在初始化它的时候,必须确保父元素和子元素的内容已经正确渲染了。如果子元素或者父元素DOM结构发生改变的时候,必须重新调用scroll.refresh()方法重新计算来确保滚动效果的正常。所以better-scroll不能滚动的原因多半是初始化better-scroll的时机不对,或者是当DOM结构发送变化的时候并没有重新计算better-scroll。
(3)better-scroll结合Vue
Vue.js提供了我们一个获取DOM对象的接口——vm.$refs。在这里,我们通过了this.$refs.wrapper访问到了这个DOM对象,并且我们在mounted这个钩子函数里,this.$nextTick的回调函数中初始化better-scroll。因为这个时候,wrapper的DOM已经渲染了,我们可以正确计算它以及它内层content的高度,以确保滚动正常。
这里的this.$nextTick是一个异步函数,为了确保DOM已经渲染,底层用到了MutationObserver或者是setTimeout(fn,0)。其实我们在这里把this.$nextTick替换成setTimeout(fn,20)也是可以的(20ms是一个经验值,每一个Tick约为17ms),对用户体验而言都是无感知的。
(4)异步数据的处理
在我们的实际工作中,列表的数据往往都是异步获取的,因此我们初始化better-scroll的时机需要在数据获取后,代码如下:
{{item}}
这里的requestData是伪代码,作用就是发起一个http请求从服务端获取数据,并且这个函数返回的是一个promise(实际项目中我们可能会用axios或者vue-resource)。我们获取到数据的后,需要通过异步的方式再去初始化better-scroll,因为Vue是数据驱动的,Vue数据发生变化(this.data=res.data)到页面重新渲染是一个异步的过程,我们的初始化时机是要在DOM重新渲染后,所以这里用到了this.$nextTick,当然替换成setTimeout(fn,20)也是可以的。
注意:这里为什么是在created这个钩子函数里请求数据而不是放到mounted的钩子函数里?因为requestData是发送一个网络请求,这是一个异步过程,当拿到响应数据的时候,Vue的DOM早就已经渲染好了,但是数据改变—>DOM重新渲染仍然是一个异步过程,所以即使在我们拿到数据后,也要异步初始化better-scroll。
使用
初始化需要滚动的dom结构
借助ref属性用来绑定某个dom元素,或者来说来绑定某个组件,然后在函数内用this.$refs.menuwrapper获取到dom。
说明:如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例:
在ajax内执行_initScroll()函数
在此之前我们要做一些准备和注意事项
(1)dom结构完全加载完再调用_initScroll()方法才会生效
(2)因为要监听内容区域的高度,所以初始化应在created过程中去监听dom结构是否完全加载,这里是在$nextTick对象中进行触发检测
ES6语法格式:this.$nextTick(()=>{})
created(){//在实例创建完成后被立即调用$el属性目前不可见。
axios.get('static/data.json').then((result)=>{
this.goods=result.data.goods
//dom结构加载结束
this.$nextTick(()=>{
this._initScroll();//初始化scroll
})
})
}
(3)在methods方法里面定义一个_initScroll的函数,主要用来对左右两侧dom结构进行初始化
methods:{
//用来对左右两侧dom结构进行初始化
_initScroll(){
//实例化better-scroll插件,传入要滚动的DOM对象
this.meunScroll=newBScroll(this.$refs.menuWrapper,{
click:true
});
this.foodScroll=newBScroll(this.$refs.foodsWrapper,{
click:true
});
}
}
说明:vue中更改数据,DOM会跟着做映射,但vue更新DOM是异步的,用$nextTick()来确保Dom变化后能调用到_initScroll()方法。调用_initScroll()方法能计算内层ul的高度,当内层ul的高度大于外层wrapper的高度时,可以实现滚动。
此时俩侧可以分别滚动了!
(4)实现左右联动
原理:我们计算出右侧实时变化的y值,落到哪一个区间,我们就显示那一个区间。首先我们要计算整体区间的一个高度,然后分别计算第一个区间的高度,第二个区间的高度,以此类推。然后将区间数存入一个定义好的数组。当我们在滚动的时候实时拿到y轴的高度,然后对比在哪一个区间,这样我们就会得到一个区间的索引值去对应左侧的菜品类别,最后我们用一个vue的class去绑定高亮文本。
1.定义一个方法在_initScroll下面,作为计算高度的方法叫做_calculateHeight(),再定义一个listHeight:[]数组,存放获取到的每一块foods类的高度。然后通过给每个li定义类名来供js选择从而计算出高度存放到listHeight数组里。
//通过方法计算foods内部每一个块的高度,组成一个数组listHeight。
//每个li定义一个类food-list-hook通过获取该类来计算每一块的高度存到数组listHeight里
_calculateHeight(){
//获取li通过food-list-hook
letfoodList=this.$refs.foodsWrapper.querySelectorAll(".food-list-hook");
letheight=0;//初始化高度
this.listHeight.push(height)//把第一个高度存入数组
//通过循环foodList下的dom结构,将每一个li的高度依次送入数组
for(leti=0,l=foodList.length;i
2.我们获取到区间高度数组后,我们要实时获取到右侧的y值,和左侧的索引值做一个对比,定义一个scrollY变量用来存放实时获取的y值。bs插件为我们提供了一个实时获取y值的方法,我们在初始化this.foodScroll的时候加一个·属性probeType:3,其作用就是实时获取y值,相当于探针的作用。
goods:[],//goodsjson数组
listHeight:[],//存放foods内部的每一块的高度
scrollY:0
this.foodScroll=newBScroll(this.$refs.foodsWrapper,{
click:true,
//探针作用,实时监测滚动位置
probeType:3
});
3.我们再添加一个方法this.foodScroll.on('scroll',(pos)=>{}),作用是实时滚动的时候把获取到的位置给暴露出来。代码如下。
//结合BScroll的接口使用,监听scroll事件(实时派发的),并获取鼠标坐标,当滚动时能实时暴露出scroll
this.foodScroll.on("scroll",(pos)=>{//回调函数
//scrollY接收变量
this.scrollY=Math.abs(Math.round(pos.y))//滚动坐标会出现负的,并且是小数,所以需要处理一下,实时取得scrollY
//console.log(pos.y)
})
4.定义一个计算属性computed,获取到food滚动区域对应的menu区域的子块的索引i值,从而定位到左侧边栏的位置。
computed:{
currentIndex(){//计算到达哪个区域的区间的时候的对应的索引值
//利用listHeight存放每一块对应的高度
for(leti=0,l=this.listHeight.length;imenuHeight_fir&&this.scrollY
获取到i后,,然后通过设置一个class来做样式切换变化:class="{'current':currentIndex===index}",当currentIndex和menu-item对应的index相等时,设置current的样式。这样就可以实现左右联动了。
...
在样式里提前设好选中和正常的样式
5.最后实现左侧点击的功能。在左侧的li下绑定一个selectMenu的点击事件,并传入索引值,这样我们就可以知道点击的是哪一个li
...
selectMenu(index,event){//点击左侧,右侧响应
this.foodScroll.scrollTo(0,-this.listHeight[index],300)
}
scrollTo(x,y,time,easing)
//滚动到某个位置,x,y代表坐标,time表示动画时间,easing表示缓动函数
scroll.scrollTo(0,500)
参考:vue使用better-scroll的参数和方法
6.关于在selectMenu中点击事件
在selectMenu中点击,在pc界面会出现两次事件,在移动端就只出现一次事件的问题
原因:better-scroll会监听事件(例如touchmove,click之类),并且阻止默认事件(preventstop),并且他只会监听移动端的,pc端的没有监听
在pc页面上better-scroll也派发了一次click事件,原生也派发了一次click事件
//better-scroll的事件,有_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…}
解决:针对better-scroll的事件,有_constructed:true,所以做处理,return掉非better-scroll的事件
selectMenu(index,event){
if(!event._constructed){//去掉自带的click事件点击,即pc端直接返回
return;
}
letfoodList=this.$refs.foodsWrapper.querySelectorAll(".food-list-hook");//获得监听元素
letel=foodList[index];//获得当前监听元素的高度
this.foodScroll.scrollToElement(el,300);//类似jumpto的功能,通过这个方法,跳转到指定的dom
}
goods组件到此差不多了!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。