几行代码带你实现几万条数据的虚拟滚动

一、先理解虚拟滚动的原理

虚拟滚动其实就是综合数据分页和无限滚动的方法,在有限的视口中只渲染我们所能看到的数据,超出视口之外的数据就不进行渲染,可以通过计算可视范围内的但单元格,保证每一次滚动渲染的DOM元素都是可以控制的,不会担心像数据分页一样一次性渲染过多,也不会发生像无限滚动方案那样会存在数据堆积,也就是按需渲染。 大致如图(网上找的):

二、代码实现

1、简单div布局
css 复制代码
<div className="table" ref={tableRef}>
      <div
        className="table-scroll"
        style={{ height: height + 'px' }}
        ref={scroll}
        onScroll={onScroll}
      >
        <div
          className="table-contont"
          style={{
            height: dataSource.length * itemHeight,
          }}
        >
          <div className="table-list" ref={list}>
             {renderList.map((item) => {
              return <div className="table-item" key={item}>{item + ''}测试 </div>
            })}
          </div>
        </div>
      </div>
 </div>
 // css 部分
 .table {
  width: 100%;
  height: 600px;
}

.table-scroll {
  height: 100%;
  overflow-y: scroll;
}

.table-item {
  height: 60px;
  background-color: salmon;
  margin: 5px;
  color: white;
  border-radius: 40px;
  padding-left: 23px;
  text-align: left;
  line-height: 60px;
}
2、初始化数据
scss 复制代码
  const [dataSource, setDataSource] = useState([]); // 默认的所有数据
  const [position, setPosition] = useState([0, 0]); // 定义可视范围内的数据

  const tableRef = useRef(null);
  const scroll = useRef(null);
  const list = useRef(null);

  const scrollInfo = useRef({
    height: 600,
    itemHeight: 60,
    renderCount: 0,
    bufferCount: 8,
  }) // 默认参数项

  useEffect(() => {
    const { height, itemHeight, bufferCount } = scrollInfo.current
    const dataSource = new Array(10000).fill(1).map((item, index) => index + 1)
    const renderCount = Math.ceil(height / itemHeight) + bufferCount
    scrollInfo.current = { renderCount, height, bufferCount, itemHeight }
    setDataSource(dataSource)
    setPosition([0, renderCount])
  }, [])
  
  const { itemHeight, height } = scrollInfo.current
  const [start, end] = position
  const renderList = dataSource.slice(start, end) /* 渲染区间 */

在初始化时 useEffect 所做的事情主要是

a、先定义1万条数据

b、通过可视范围的高度和每一条数据的高度,再加上 bufferCount 计算出初始化时候,页面需要加载的数据范围

3、模拟用户滚动行为,在用户滚动滑轮或者滑动屏幕时,相应的滚动列表。我们这里的滚动列表不是真正的滚动列表,而是根据滚动的位置重新渲染可见的列表元素。也就是需要给dom节点增加 onScroll 事件。

上代码

ini 复制代码
const onScroll = () => {
    const { scrollTop } = scroll.current;
    const { renderCount, itemHeight } = scrollInfo.current;
    const currentOffset = scrollTop - (scrollTop % itemHeight)
    list.current.style.transform = `translate3d(0, ${currentOffset}px, 0)` /* 偏移,造成下滑效果 */
    const start = Math.floor(scrollTop / itemHeight)
    const end = Math.floor(scrollTop / itemHeight + renderCount + 1)
    if (end !== position[1] || start !== position[0]) {
      /* 如果render内容发生改变,那么截取  */
      setPosition([start, end])
    }
  }

在onScroll滚动时主要做了这几件事

a、通过scrollTop和itemHeight设置 滚动时的 transform 下滑效果

b、在滚动时 通过scrollTop、itemHeight 和 renderCount,来计算出需要 render内容的数据截取

三、最终效果如图

相关推荐
摘星编程22 分钟前
React Native + OpenHarmony:UniversalLink通用链接
javascript·react native·react.js
qq_1777673743 分钟前
React Native鸿蒙跨平台数据使用监控应用技术,通过setInterval每5秒更新一次数据使用情况和套餐使用情况,模拟了真实应用中的数据监控场景
开发语言·前端·javascript·react native·react.js·ecmascript·harmonyos
烬头88211 小时前
React Native鸿蒙跨平台应用实现了onCategoryPress等核心函数,用于处理用户交互和状态更新,通过计算已支出和剩余预算
前端·javascript·react native·react.js·ecmascript·交互·harmonyos
程序员清洒2 小时前
Flutter for OpenHarmony:Text — 文本显示与样式控制
开发语言·javascript·flutter
雨季6663 小时前
Flutter 三端应用实战:OpenHarmony 简易“动态内边距调节器”交互模式深度解析
javascript·flutter·ui·交互·dart
天人合一peng3 小时前
Unity中button 和toggle监听事件函数有无参数
前端·unity·游戏引擎
会飞的战斗鸡3 小时前
JS中的链表(含leetcode例题)
javascript·leetcode·链表
方也_arkling4 小时前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐4 小时前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
qq_177767374 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos