写在前面
还 ⭐ 的基于 react-hooks 和 react-virtualized 写的虚拟滚动搭配上拉下滑加载的 scroll 组件
🌟🌟 github.com/DavidWong97… 🌟🌟
写出来的小玩意可能不是特别完美,如果能给大伙们提供到思路或者帮助,实在不胜荣幸
也希望大伙们给个 star 啦~
antd中的list-view
相信用过 antd-mobile 的小伙伴都知道里面的 ListView 组件
组件还算是能满足很多需要的
但遇到数据量大时可能就不太够用了
而且一些不熟悉 useMemo 的小伙伴对于 ListView 的优化可能会不知所措
所以我就封装了一个不需要自己优化 renderRow,又能做到虚拟滚动,又能满足 ListView 基本需要的组件
接下来就一步步拆解,如何实现这个组件😈
什么是虚拟滚动
1. 背景
当遇到数据量大的列表渲染,如10万条数据,一次性渲染十万个元素,内存消耗肯定是裂开的
这时候考虑怎么展示完列表,又能优化性能提升体验 —— 虚拟滚动
2. 效果描述
创建一个容器,容器内部展示固定数量的数据,通过前后索引确定渲染数据的哪个区间
react-virtualized原理剖析
1. 找出渲染区间
a. startIndex: Math.max(0, startIndex - threshold),
b. stopIndex: Math.min(rowCount - 1, stopIndex + threshold)
c. threshold为列表项目数,默认15
2. 触发渲染
_invokeRendered【渲染列表】
a. componentDidMount
b. componentDidUpdate
3. 对比后渲染
每次渲染会缓存上一次的属性,若属性【如index】不一样,重新渲染

长列表实现
1. 构成:refresh + virtualScroll + loadMore
2. 状态控制 STATS
a. init - 初始状态
b. dragging - 拖动中
c. pre-refresh - 拖动达到最大限制,刷新状态就绪
d. not-enough - 拖动未达到最大限制
e. refreshing - 松手,正在刷新
f. success - 刷新成功
3. refresh 实现
a. onTouchStart
记录startY,若状态为refreshing或success,不执行
若scrollTop不为0,不执行
b. onTouchMove
计算logo的offset -> 当前pageY - startY
根据条件设置offset
① 0 < offset && offset <= 下拉最大限制,设为计算得出的offset
② offset < 0,设为0
③ offset > 下拉最大限制,设为下拉最大限制
若 offset >= 下拉最大限制,状态设为pre-refresh
否则若 offset < 下拉最大限制,状态设为dragging
c. onTouchEnd
若STATS为 pre-refresh
有传入回调onPullDown则调用,状态变更为 refreshing
回调执行完,状态变更为 success,延迟后变为init
否则
状态变更为 not-enough,延迟后变为init
4. virtualScroll 实现
a. 渲染传入的自定义组件children
b. 若传入data有数据,渲染vList
5. loadMore 实现
vList 中的 rowRenderer
① 传入对应的参数给 row 组件并渲染
② 根据状态渲染loading和没有更多的展示
6. onScroll回调封装一层
a. 有传入onScroll,执行回调onScroll
b. 有传入onPullUp
若 clientHeight + scrollTop === scrollHeight
则到达底部,执行回调onPullUp

长列表rowRender优化
- 如果不作
useMemo
优化,列表数据其中一项只要更改,列表所有项都会重新渲染,造成性能浪费 - 对于传入的rowRender,需要独立声明一个组件包着再作渲染
- 判断的方式这里有些取巧了,直接
JSON.stringify
数据作判断
const Row = ({ row, data, index, info }:any) => {
return useMemo(() => row({ data: data, index, info }), [JSON.stringify(data[index])])
}
写在后面
看到这里的小伙伴,有没有觉得实现个长列表其实也很简单呢~
文中的解析再对照着项目中的代码看,相信你一定有所收获
还有~~~别忘了👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~