小程序动态高度长列表优化实战:解决滑动白屏与性能问题(二)
前言
本案例将涉及一个不完全
动态高度的长列表问题。所谓的不完全动态
,是指每个列表项的高度虽然不完全相同,但我们可以通过计算得到每个列表项的实际高度。列表项的高度可以根据后台返回的数据计算得出。为了实现这个功能,我们将对 taro 框架的 virtual-list
组件进行改造。
案例 · 小程序长列表点击响应延时

在应用的使用过程中,我们发现,随着列表项的不断加载,我们点击列表项的响应速度变得越来越慢
,并且能明显感觉到点击延时
的存在,我们通过工具测试一下滑动了多屏之后的性能:
- 滑动 400 条数据后,对列表项点击事件响应测试。点击响应 8s 延时
可以从上图看到,在点击之后,FPS 曲线出现了一个很大的波谷,大概8s
左右,FPS 最低值到 0
。实际的样子就是:我点击了一下列表项,页面就没法操作了,过了一会,页面跳转到了详情页...
方案
taro 框架的虚拟列表,适用于固定高度的长列表。虽然官方文档说支持动态高度,但实际用起来,发现对于高度落差较大的长列表,没法适用,因为
不能及时
获取到高度。但如果我们能够在列表获取高度时,计算得到列表项高度
,再传给 virtual-list,就能直接复用 virtual-list 原有的虚拟列表
逻辑,并解决动态高度
的问题
可以看下 taro 的 virtual-list 源码
我们不难发现,virtual-list
中的列表项的高度其实是由外部传入一个固定高度height
来确定,而我们只需要将 Taro
获取高度的逻辑,改成获取外部传入的高度,就可以解决我们的问题。而涉及的代码就就可以从 获取列表项位置
、获取列表项高度
、获取列表项偏移量
这三处代码入手。我们将 Taro 的 virtual-list
代码复制一份,开始魔改。
我们定义一个 customSize
属性,用于标识是否要用外部传入的列表高度。
关键代码
// Taro/virtual-list/react/createListComponent.js
constructor (props) {
super(props)
// ...
// 新增自定义高度逻辑
if (props.customSize) {
this.state.sizeList = this.props.sizeList
} else {
this.state.sizeList = new Array(this.props.itemCount).fill(-1)
}
}
this._getCountSize = (props, count) => {
// ...
// 外部传入自定义的 size
if (props.customSize) {
const sizes = props.sizeList.slice(0, count)
return sizes.reduce((p, a) => {
return p + this._getSize(a)
}, 0)
}
}
this._getSizeUpload = (index, isHorizontal) => {
// 新增自定义高度逻辑
if (props.customSize) {
const { sizeList } = this.props
return this._getSize(sizeList[index])
}
// ...
}
进一步优化
在魔改Taro 的 virtual-list
代码的同时,发现一个问题。当我们快速上下滑动列表页面时,即使列表已经触顶或触底了,但是不会触发相应的回调
,猜测 Taro 对列表的滑动,做了节流(没有找到相关代码,找到一个issue,但已经关闭了)
问题解决
在列表的最顶端和最低端各放置一根高度为 1px
的透明线条,我们监听着两根线条是否出现在可视区,来判断是触底还是触顶。
关键代码
// 解决scrollView置顶后,scrollOffset不为0的问题
const observePage = () => {
const observerObjTop = Taro.createIntersectionObserver().relativeToViewport({ top: -position.top });
observerObjTop.observe(`#todo-list-scroll-line-view-top`, (res) => {
const isBool = res.intersectionRatio > 0
if (isBool && resetScroll) {
resetScroll('top')
}
});
const observerObjBottom = Taro.createIntersectionObserver().relativeToViewport({ bottom: -position.bottom });
observerObjBottom.observe(`#todo-list-scroll-line-view-bottom`, (res) => {
const isBool = res.intersectionRatio > 0
if (isBool && resetScroll) {
resetScroll('bottom')
}
});
observerObjs.current = [observerObjTop, observerObjBottom]
}
成果
滑动 400 条数据后,对列表项点击事件响应测试
- 优化前,点击响应 8s
- 优化后,点击响应 3s
可以看到之前的波谷已经大大缩小到 3s
,FPS 的最低值大于 0
,内存也稳定在 73 MB。相比于优化前,性能还是提升了不少,至少不会感觉到明显的点击延迟。
最后
码字不易,各位老铁多支持: 点赞 + 收藏 + 评论
小程序性能检测工具:perfdog.qq.com
转载自:https://juejin.cn/post/7270829887530827839