如何实现虚拟滚动

js实现虚拟滚动

当我们有一个大型数据需要以列表的形式渲染到页面上的时候,由于生生了大量的dom从而导致页面性能降低,出现页面渲染慢,滚动卡动等问题。

虚拟列表做的事就是确保性能的前提下,采用虚拟滚动来进行性能优化。

传统列表渲染

这里我们模拟一个10万条的大型列表数据:
传统列表数据
html dom结构如下:
电脑有点拉垮哈,此时风扇已经开始疯狂运转。
在这里插入图片描述
如果这里再增加一些交互操作,浏览器都有可能崩溃。

虚拟滚动的原理

今天我们来实现一个最简单的可视区高度固定单条数据高度固定且每条数据的高度都固定
实现原理分为一下几步:

  1. 计算容器高度:计算需滚动元素所在的容器高度(指定的某个可视区域高度或浏览器窗口高度);
  2. 创建占位元素:占位元素的高度等于实际列表的总高度,但不包括实际数据,只用于撑开滚动条,达到滚动效果;
  3. 确定容器可容纳的数据条数:根据容器高度与每条数据所占高度,确定容器内同一时刻可视的数据条数;
  4. 监听滚动事件:当监听到滚动事件时,根据当前的滚动位置与数据高度,计算需要展示在可视区域中的数据内容;
  5. 渲染:将上一步中确定的当前数据内容渲染在界面的可视区域内。
    在这里插入图片描述

初始化渲染数据

单条数据高度确定itemSize,可视区高度确定200px。可以用二者计算出显示条数。初始索引为0,结束索引为显示条数。之后截取总数据当中对应的数据即可。

// 可显示的列表项数
   visibleCount () {
      return Math.ceil(this.screenHeight / this.itemSize)
   },
...
  mounted () {
    // 开始索引
    this.start = 0
    // 结束索引
    this.end = this.start + this.visibleCount
  },

滚动逻辑

可视区高度确定,列表需要模拟出滚动条,这里采用占位div撑开方案。随着滚动条滚动,监听滚动高度计算出开始索引和结束索引,再计算出滚动偏移量,再利用translate3d滚动,translate3d且因为没有重排重绘,所以性能更好

methods: {
  scrollEvent () {
    // 当前滚动位置
    const scrollTop = this.$refs.list.scrollTop
    // 此时的开始索引
    this.start = Math.floor(scrollTop / this.itemSize)
    // 此时的结束索引
    this.end = this.start + this.visibleCount
    // 此时的偏移量 
    this.startOffset = scrollTop - (scrollTop % this.itemSize)
  }
}

再渲染逻辑

随着滚动条滚动,监听滚动高度计算出开始索引和结束索引,重置开始和结束索引,也就自然引发Vue重新渲染。

现在不同框架下均有虚拟滚动工具可用,比如:react-virtualized、vue-virtual-scroll-list、ngx-virtual-scroll等等。

### 移动端虚拟滚动实现方法 对于移动端应用,在处理大量数据展示时,传统方式会遇到性能瓶颈。当DOM节点数量达到数千甚至更多时,页面渲染速度显著下降并可能导致卡顿现象[^2]。 #### 使用 Better-Scroll 实现虚拟滚动 Better-Scroll 是一款专为移动Web设计的强大滚动库,能够有效提升用户体验。通过该库提供的`observeDom`选项以及自定义事件监听机制,可以轻松构建高效的虚拟列表组件[^1]: ```javascript import BScroll from '@better-scroll/core' import ObserveDOM from '@better-scroll/observe-dom' BScroll.use(ObserveDOM) const bs = new BScroll('.wrapper', { observeDOM: true, // other options... }) ``` 为了进一步优化性能表现,还可以结合`pullup`加载功能,仅在接近底部时动态获取新一批次的数据项,从而减少初始加载量。 #### Vue 中的 Virtual Scroll 组件实践 针对Vue框架下的应用场景,可采用专门用于解决此类问题的第三方包如vue-virtual-scroller 或者基于官方推荐的方式自行封装逻辑。核心思路在于维护一个可视窗口内的最小化DOM结构,并随着用户的滑动操作实时更新显示内容[^3]。 ```html <template> <div class="virtual-list"> <!-- 可视区域 --> <ul ref="listContainer"></ul> </div> </template> <script> export default { data() { return { visibleItems: [], // 当前可见元素集合 itemHeight: 50, // 单个item高度假设固定不变 startIndex: 0 // 开始索引位置 } }, methods: { updateVisibleItems(scrollTop) { const { height } = this.$refs.listContainer.getBoundingClientRect() let endIndex = Math.ceil((scrollTop + height) / this.itemHeight) this.startIndex = Math.floor(scrollTop / this.itemHeight) this.visibleItems = Array.from({ length: endIndex - this.startIndex }).map( (_, i) => ({ id: this.startIndex + i }) ) } } } </script> ``` 此段代码展示了如何计算当前应该呈现给用户的那一部分列表片段,并将其映射到实际HTML模板中去。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值