本节主要讲解 scroll-view 在页面上的使用,长列表的渲染。
# scroll-view 滑动效果实现头部导航栏

导航的滑动区域需要实现左右滑动效果,左右滑动这个在信息资讯设计下很常见,可以选用 Uniapp 中基础组件 scroll-view 容器,在遇到与左右或者上下互动局部内容的时候,可以考虑组件 scroll-view,配置查看scroll-view (opens new window),示例代码:
<scroll-view class="scroll-view" scroll-x="true" scroll-left="120">
<view class="item uni-bg-red">A</view>
<view class="item uni-bg-green">B</view>
<view class="item uni-bg-blue">C</view>
</scroll-view>
在方位x轴上进行参数设置,即设置 scroll-x="true",即可实现,左右滑动,一切就是那么简单...
但是当你以为一切就这样搞定的时候,可是并没有左右排列,肿么办?

其实是关于 scroll-view 失效的问题,复制官方代码代码后会发现 scroll-view 横向滚共不生效,其实是没有设置好样式,将 scroll-view 容器设置宽度,并设置 white-space: nowrap; scroll-view 容器的每一项都设置宽度和 display:inline-block:
.scroll-view {
width: 100%;
white-space: nowrap;
.item {
position: relative;
display: inline-block;
width: 218rpx;
padding-bottom:16rpx;
margin-right: 16rpx;
line-height: 34rpx;
}
}
# 通过绑定 class/style 实现active滑块

这块的交互是点击触发实现滑块滑动至被触发的项,以及被触发的项实现样式切换。
<scroll-view class="scroll-view" scroll-x>
<view class="item" v-for="(item, index) in navList" :class="{active: curNav == item.id}" :key="item.id" @click="switchNav(item.id, index)">
<view class="desc">{{item.name}}</view>
</view>
<view class="slide" :style="'width: '+ sliderWidth +'rpx;transform: translateX('+ sliderOffset +'rpx)'"></view>
</scroll-view>
// 导航切换
switchNav(id, index) {
this.curNav = id
this.sliderWidth = index == 1 ? 140 : 60;
this.sliderOffset = 126 * index + (index > 1 ? 80 : 16) + (index == 0 && 16);
// 加载数据
this.getRelatedVideo(id)
}
.scroll-view {
position: fixed;
top: 0;
width: 100%;
white-space: nowrap;
text-align: center;
line-height: 86rpx;
color: #333;
.item {
position: relative;
display: inline-block;
min-width: 126rpx;
padding: 0 20rpx;
line-height: 34rpx;
padding-bottom: 16rpx;
<!-- 触发项样式 -->
&.active{
color:#f32628;
}
}
}
<!-- 滑块样式 -->
.slide {
position: absolute;
width: 60rpx;
height: 4rpx;
left: 0;
bottom: 0rpx;
background: #f32628;
transition: transform 0.3s;
}
触发项的样式由 :class="{active: curNav == item.id}" 判定,active 这个 class 存在与否将取决于数据属性 curNav 是否等于为当前触发项的 id。class 的值将会添加 on,结果渲染为:
<view class="item active"></view>
对应上类名 active 的样式即可实现触发项样式的切换。
底部滑块的动画需设置 transition: transform 0.3s; 样式,只要改变 transform 属性值 translateX 即可实现左右动画。关键在于样式的绑定改变:
<view class="slide" :style="'width: '+ sliderWidth +'rpx;transform: translateX('+ sliderOffset +'rpx)'"></view>
// 导航切换
switchNav(id, index) {
this.curNav = id
this.sliderWidth = index == 1 ? 140 : 60;
this.sliderOffset = 126 * index + (index > 1 ? 80 : 16) + (index == 0 && 16);
//
this.getRelatedVideo(id)
}
点击导航判定触发项的索引,计算出滑块偏移位置 translateX 的值即可。与 :class="{active: curNav == item.id}" 不同的是,:style 由拼接字符决定整个值。
# 长列表 list 的渲染

长列表的数据直接是请求后台列表数据,再用 v-for 循环遍历渲染到页面上:
<view class="video-list">
<view class="video-item" v-for="item in relatedVideo">
<view class="video-wrap">
<image class="img" :src="item.coverUrl + $imgSuffix"></image>
<view class="desc ellipsis">{{item.title}}</view>
<view class="creater-bar flex-box">
<view class="avactor-box flex-box">
<image class="avactor" src=""></image>
<view class="name">{{item.creator[0].userName}}</view>
</view>
<view class="more">
<view class="iconfont"></view>
</view>
</view>
</view>
</view>
</view>
import { apiGetRelatedVideo } from '@/apis/index.js';
export default {
data() {
return {
relatedVideo: [],
};
},
methods: {
// 获取相关视频
getRelatedVideo(id) {
this.curNav = id
const params = {
id
}
apiGetRelatedVideo(params).then(res => {
this.relatedVideo = res.data
})
},
}
}
列表项有个更多的三点图标(上图箭头所指)是由 iconfont 渲染而成,只要在 iconfont 项目库中加入这个图标并在 Uniapp 项目引入直接可以使用:
<view class="iconfont"></view>
提示:关于 Uniapp 项目如何引入 iconfont 的操作,可查看基础课程《使用 iconfont 作为整站图标》章节。
# 列表处理下拉更新,上拉加载
移动端长列表浏览是一个非常常见的浏览交互,用户可以下拉刷新当前第一页的数据,也可以滑动到底部(上拉)无限加载数据。在 Uniapp 项目中一般使用 scroll-view 可滚动视图区域组件实现多端下的浏览。scroll-view 可以轻松解决滚动到顶部/底部的事件处理。
但是用 scroll-view 处理长列表很容易引发性能问题,不仅要判断偏移位置,而且要响应交互回调,我们在处理复杂多变的功能时,可以在官方插件市场 (opens new window)寻找适合功能的插件,通过示例改成适用自己项目的功能。关于下拉刷新上拉加载组件 (opens new window),我这选用了一个插件 mescroll (opens new window),比较合适当前有导航栏和长列表组成的页面。
相关代码:
<!-- top="86" 向下偏移导航栏的高度 -->
<mescroll-uni top="86" :down="downOption" :up="upOption" @down="downCallback" @up="upCallback">
<view class="video-list">
<view class="video-item" v-for="(item, index) in relatedVideo" :key="index">
<view class="video-wrap">
<image class="img" :src="item.coverUrl + $imgSuffix"></image>
<view class="desc ellipsis">{{item.title}}</view>
<view class="creater-bar flex-box">
<view class="avactor-box flex-box">
<image class="avactor" src=""></image>
<view class="name">{{item.creator[0].userName}}</view>
</view>
</view>
</view>
</view>
</view>
</mescroll-uni>
// ...
data() {
return {
// 下拉刷新的常用配置
downOption: {
auto: false // 是否在初始化后,自动执行下拉回调callback; 默认true
},
// 上拉加载的常用配置
upOption: {
auto: false, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
page: {
num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
size: 10 // 每页数据的数量,默认10
},
},
}
},
methods: {
// ...
/*下拉刷新的回调 */
downCallback(mescroll) {
mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
},
/*上拉加载的回调: mescroll携带page的参数, 其中num:当前页 从1开始, size:每页数据条数,默认10 */
upCallback(mescroll) {
//联网加载数据
this.getList(mescroll.num, mescroll.size, (res) => {
//设置列表数据
if(mescroll.num == 1) this.relatedVideo = []; //如果是第一页需手动置空列表
this.relatedVideo = this.relatedVideo.concat(res)
// 后台接口有返回列表的总数据量 totalSize
// mescroll.endBySize(10, 50); //必传参数(当前页的数据个数, totalSize总数据量)
mescroll.endSuccess();
}, () => {
//联网失败的回调,隐藏下拉刷新的状态
mescroll.endErr();
})
}
}
可以看到下图中下拉状态的交互样式。
在引用这个插件前,由于多端的差异化太大,需要对 pages.json 进行配置,只配置使用 mescroll-uni 的页面,可以解决取消 ios 回弹,避免和下拉刷新冲突等问题。
"path": "pages/video/index",
"style": {
"navigationBarTitleText": "视频",
"app-plus":{
"titleNView": false,
"bounce" : "none", // 取消 ios 回弹,避免和下拉刷新冲突
"disableScroll": true // Android小程序卡顿
}
}
页面初始化时,如果一次性向视图层传递大量的数据,使视图层一次性渲染大量节点,可能造成通讯变慢、页面切换卡顿,所以建议以局部更新页面的方式渲染页面。如:服务端分页;服务端返回100条数据,进行分批加载,一次加载50条,500ms 后进行下一次加载。
其实 scroll-view 不适合放长列表,有性能问题。长列表滚动和下拉刷新,应该使用原生导航栏搭配页面级的滚动和下拉刷新实现。包括在 app-nvue 页面,长列表应该使用 list 而不是 scroll-view。当前为了页面效果,权衡舍弃了页面级的滚动。
如果是在 app-nvue 下处理长列表,使用 list 组件的性能高于使用 view 或 scroll-view 的滚动。原因在于 list 在不可见部分的渲染资源回收有特殊的优化处理。<list> 组件是提供垂直列表功能的核心组件,拥有平滑的滚动和高效的内存管理,非常适合用于长列表的展示。注意当前是 vue 项目,不是 app-nvue 项目,因此未使用 list 完成长列表功能。
# 小结
- scroll-view 的左右滑动需要样式设置,关键在于
white-space: nowrap; - 点击切换的常用功能通常操作元素的 class,传给
:class一个对象,以动态地切换 class 值; - scroll-view 可以处理列表的下拉更新,上拉加载功能,可以找现成的功能插件完成开发。
- 本章代码 uni-course-实战开发视频页 (opens new window)。