在移动端的项目中,由于显示器容量的限制,经常会有许多滚动场景的需求,因此Vue项目中实现列表的上拉加载和下拉刷新是非常频繁的事情,但是在真正项目中前端对于这个的实现有非常多的坑,今天主要介绍Vue项目中插件cube-scroll、better-scroll和mescroll的使用以及滚动场景中的业务逻辑设计。
在了解这些滚动插件之前,我们首先需要知道浏览器的滚动原理:
浏览器的视口的宽高是有限的,当页面的内容高度超过视口高度(contentHeight > viewHeight
)的时候,会出现纵向滚动条;同样地,当页面的内容宽度超过视口宽度(contentWidth > viewWidth
)的时候,会出现横向滚动条。总之就是当视口展示不下页面上内容的时候,会通过滚动条的形式让用户看到剩余的内容。
content是被列表内容弹性撑开的,超过了父容器的宽度(高度)才得以滚动。
Better-Scroll——基于iScroll的重写和实现
Better-Scroll的文档
应用场景: 滚动列表、轮播图、picker
1. 初始化better-scroll
import BScroll from 'better-scroll'
let wrapper = document.querySelector('.wrapper')
let scroll = new BScroll(wrapper, {}) // 初始化就是new一个BScroll类的实例
better-scroll 的初始化时机很重要,因为它在初始化的时候,会计算父元素和子元素的高度和宽度,来决定是否可以纵向和横向滚动。因此,我们在初始化它的时候,必须确保父元素和子元素的内容已经正确渲染了。如果子元素或者父元素 DOM 结构发生改变的时候,必须重新调用 scroll.refresh() 方法重新计算来确保滚动效果的正常。
2. Vue中的使用
better-scroll可以很好地和Vue配合使用,在异步获取数据和实现动态更新的时候发挥着强大的作用。
使用better-scroll配合Vue实现下拉刷新的示例:
import BScroll from 'better-scroll'
export default{
data(){
return {
data: []
}
},
created(){
this.loadData()
},
methods:{
loadData(){
requestData().then((res) =>{
this.data = res.data.concat(this.data)
this.$nextTick(() => {
if(!this.scroll){
this.scroll = new BScroll(this.$refs.wrapper, {})
this.scroll.on('touchend', (pos) =>{
// 下拉动作,派发touchend时间监听这个动作
if(pos.y > 50){
this.loadData() // 重新请求数据
}
})
}else{
this.scroll.refresh()
}
})
})
}
}
}
但是以上代码中存在大量的命令式的代码,如果项目中存在着许多类似滚动组件,就会写很多类似的命令式的重复的代码,并且在上面的代码中数据请求和better-scroll之间也是做了强耦合,这样在Vue中并不是很推荐。
因此最好的办法是封装scroll组件,并在全局注册,之后再使用就很方便
使用Js对 better-scroll 做一层 Vue 的封装:通过 props 的形式,把一些对 better-scroll 定制化的控制权交给父组件;通过 methods 暴露的一些方法对 better-scroll 的方法做一层代理;通过 watch 传入的 data,当 data 发生改变的时候,在适当的时机调用 refresh 方法重新计算 better-scroll 确保滚动效果正常,这里之所以要有一个 refreshDelay 的设置是考虑到如果我们对列表操作用到了 transition-group 做动画效果,那么 DOM 的渲染完毕时间就是在动画完成之后。
在全局注册了的Scroll组件之后,我们可以更加简单的使用:
import BScroll from 'better-scroll'
export default{
data(){
return {
data: [],
pulldown: true
}
},
created(){
this.loadData()
},
methods:{
loadData(){
requestData().then((res) =>{
this.data = res.data.concat(this.data)
})
}
}
}
3. 可能出现的问题
- better-scroll不能滚动
A:better-scroll的初始化时机不对,或者是当DOM结构发生变化的时候没有重新的计算better-scroll - 手指触摸一下页面会滑动很多
A:将容器的设置-webkit-overflow-scrolling: touch
样式删除掉 - 需要横向滚动的地方不能滚动
A:添加evenetPassthrough:'horizontal
配置,参考官方文档
Cube-Scroll——cube-ui的Scroll组件
在我们的项目开发中,很多都是基于cube-ui
上的组件,简洁、美观、开源。安装:npm install cube-ui -S
cube-ui是滴滴公司的技术团队基于Vue.js实现的精致移动端组件库。虽然组件不是非常的全面,但是足够应对基本的使用场景。
关于Cube-Scroll: 使用说明
Cube-ui上的Scroll实际上就是基于better-scroll进行封装的组件,Scroll支持通过插槽自定义内容和样式,可以更加便捷的配置,关于具体的使用,文档中写的非常详细了,只是举个
Scroll在默认情况下是没有下拉刷新和上拉加载的,因此需要options
传递配置项pullDownRefresh
和pullUpLoad
开启相应的功能,由computed
计算属性options实现动态监听。开启之后,上下拉时,才会触发相应的动画并派发事件,@监听事件之后实现数据的刷新和加载。
computed:{
options(){
return{
pullDownRefresh: this.pullDownRefreshObj,
pullUpLoad: this.pullUpLoadObj,
scrollbar: true
}
}
}
针对pullDownRefresh
和pullUpLoad
,都在methods
中由我们根据项目功能需求自定义,具体配置见文档。
MScroll ——精致的下拉刷新和上拉加载JS框架
MScroll官方文档
MScroll组件主打精致和好用,其中封装着许多好用的方法,易用性和可重用性强、兼容性强可以多端运行。
相较于前面两个Scroll插件,它的优点很多:
- 一个界面可多个mescroll
- 可以自动判断列表有无数据
- 自动控制翻页,不需要手动判断页码,时间等
- 可以实现更加平滑缓冲效果,并且一键滚动到顶部或者底部
- 支持图片的懒加载
- 数据不满屏自动加载下一页
- 路由跳转前后可以记录列表的滚动为止
安装:npm install --save mescroll.js
Vue中引用组件:import MescrollVue from 'mescroll.js/mescroll.vue'
使用示例:
- data: 可以配置许多操作,定义mescroll对象,配置下拉和上拉的页码与页面容量之类…
- methods:下拉刷新回调
downCallback()
,上拉加载回调upCallback()
,mescroll组件的初始化回调mescrollInit(mescroll)
- beforeRouteEnter(),beforeRouteLeave():配置顶部按钮或者isBounce时才用写,
beforeRouteLeave()
是记录列表滚动的位置,隐藏顶部按钮和isBounce的配置,beforeRouteEnter()
则是滚到原来的列表位置,恢复顶部按钮和isBounce的配置
更多的配置可以看其mescroll API文档
相比之下,Mescroll更能够适应多需求的项目的发展,能够更好地增强用户的体验,实现流畅智能的交互。
无论是使用何种插件,在逻辑方面:
- 下拉刷新的时候,重新请求data, 如果data发生变化,new_data重新覆盖old_data,否则data不变;
- 上拉加载的时候,请求新页码下的new_data,添加到本地到data上,扩充content的内容,不必将data全部重新请求。
- 滑到底部无新数据时,显示提示文字,例如“已经加载完毕”,至于这个判断,在前两个scroll组件中,需要我们手动计算判断data的最大页面数
maxPage = Math.floor(data.length/perPageNum) + 1
,防止滑动时页码超过最大页码;而在mescroll组件中可以实现自动判断是否已经没有数据了,无须手动配置和计算最大页面,十分便捷
这些组件的都为我们的项目开发带来了许多便利,同时结合组件的方法实现对数据的axios请求也是非常好用的~在熟悉使用api的同时,了解组件的源码对于我们开发中遇到的问题也非常有帮助