编程 如何实现虚拟滚动

2024-11-18 20:50:47 +0800 CST views 450

如何实现虚拟滚动

(本文使用 Vue 3 编写)

当我们面对大量数据时,传统的渲染方式会导致浏览器负载过大,页面渲染速度慢,滚动卡顿等问题。为此,虚拟滚动技术成为一种有效的性能优化手段。本文将介绍如何在 Vue 3 中实现虚拟滚动。

目录

  1. 传统渲染方式
  2. 虚拟滚动的原理
  3. 虚拟滚动的实现(元素固定高度)
  4. 使用工具(vue3-virtual-scroll-list

传统渲染方式

假设我们要渲染 10 万条数据,传统的渲染方式如下:

<template>
  <div class="parent-box">
    <div v-for="_, index in listData" class="list-item">
      {{ `item - ${index + 1}` }}
    </div>
  </div>
</template>

<script setup>
const listData = new Array(100000)
</script>

问题:一次性渲染 10 万条数据会给浏览器带来极大的压力,导致页面滚动卡顿甚至崩溃。


虚拟滚动的原理

虚拟滚动通过减少一次性渲染的 DOM 数量来优化性能,核心原理包括:

  1. 计算容器高度:计算可视区域高度。
  2. 创建占位元素:通过创建等于总数据高度的占位元素撑开滚动条,达到虚拟滚动效果。
  3. 确定可容纳的数据条数:根据容器高度和单个元素高度计算可视区域内显示的条数。
  4. 监听滚动事件:当滚动时,计算当前滚动位置并渲染对应的数据。
  5. 渲染可视区域数据:只渲染可视区域内的数据,避免一次性渲染所有数据。

虚拟滚动的实现(元素固定高度)

实现固定高度的虚拟滚动如下:

<template>
  <div class="parent-box" @scroll="getVisibleData">
    <div class="position-total"></div>
    <div class="list-item-box">
      <div v-for="value in visibleData" class="list-item">
        {{ `item - ${value}` }}
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const lineHeight = 25
const totalData = Array.from({ length: 100000 }, (_, index) => index + 1)
const visibleData = ref([])

onMounted(() => {
  getVisibleData()
})

const getVisibleData = () => {
  const parentBox = document.querySelector('.parent-box')
  const visibleDataCount = Math.ceil(parentBox.clientHeight / lineHeight)
  const startIndex = Math.floor(parentBox.scrollTop / lineHeight)

  const listItemBox = document.querySelector('.list-item-box')
  listItemBox.style.top = (startIndex * lineHeight) + 'px'

  visibleData.value = totalData.slice(startIndex, startIndex + visibleDataCount)
}

const lineHeightPx = lineHeight + 'px'
const totalHeightPx = lineHeight * totalData.length + 'px'
</script>

<style lang="scss" scoped>
.parent-box {
  position: relative;
  height: 400px;
  width: 300px;
  overflow: auto;
  border: 2px solid rgb(87, 87, 87);
  .position-total {
    height: v-bind(totalHeightPx);
  }
  .list-item-box {
    position: absolute;
    width: 100%;
    .list-item {
      height: v-bind(lineHeightPx);
      border-bottom: 1px solid rgb(223, 223, 223);
    }
  }
}
</style>

说明

  • parent-box:容器用于监听滚动事件。
  • list-item-box:虚拟滚动中实际显示的元素。
  • 当用户滚动时,只渲染可视区域内的数据,并调整 list-item-boxtop 值来更新滚动位置。

使用工具(vue3-virtual-scroll-list

对于更复杂的需求,可以使用现成的虚拟滚动工具,比如 vue3-virtual-scroll-list

引入

import VueVirtualScroller from 'vue3-virtual-scroll-list'
import App from './App.vue'

const app = createApp(App)
app.component('vue-virtual-scroller', VueVirtualScroller).mount('#app')

使用

<template>
  <div class="parent-box">
    <vue-virtual-scroller
      style="height: 100%; overflow-y: auto;"
      data-key="id"
      :data-sources="totalData"
      :data-component="ListItem"
      :keeps="20"
    />
  </div>
</template>

<script setup>
import ListItem from './listItem.vue'

const totalData = Array.from({ length: 100000 }, (_, index) => ({ id: index + 1 }))
</script>

<style lang="scss" scoped>
.parent-box {
  position: relative;
  height: 400px;
  width: 300px;
  border: 2px solid rgb(87, 87, 87);
}
</style>

ListItem 组件

<template>
  <div class="list-item">
    {{ `item - ${source.id}` }}
  </div>
</template>

<script setup>
defineOptions({ name: 'ListItem' })

const props = defineProps({
  source: {
    type: Object,
    default: () => ({})
  }
})
</script>

<style lang="scss" scoped>
.list-item {
  border-bottom: 1px solid rgb(223, 223, 223);
}
</style>

说明

  • vue-virtual-scroller 提供了简单高效的虚拟滚动实现,只需提供数据源和子组件即可轻松实现虚拟滚动效果。
  • ListItem 子组件用于展示每一条数据。
    images

结语

虚拟滚动是前端性能优化的利器,特别是在大量数据渲染时,通过减少一次性渲染的 DOM 数量,显著提升了页面的性能。可以根据具体项目需求选择手动实现或使用现成的工具来优化数据渲染的效率。

复制全文 生成海报 前端开发 性能优化 Vue.js

推荐文章

微信小程序热更新
2024-11-18 15:08:49 +0800 CST
mysql关于在使用中的解决方法
2024-11-18 10:18:16 +0800 CST
go错误处理
2024-11-18 18:17:38 +0800 CST
api接口怎么对接
2024-11-19 09:42:47 +0800 CST
2025,重新认识 HTML!
2025-02-07 14:40:00 +0800 CST
Rust开发笔记 | Rust的交互式Shell
2024-11-18 19:55:44 +0800 CST
Nginx 反向代理
2024-11-19 08:02:10 +0800 CST
JavaScript中的常用浏览器API
2024-11-18 23:23:16 +0800 CST
Python Invoke:强大的自动化任务库
2024-11-18 14:05:40 +0800 CST
Vue3中如何处理异步操作?
2024-11-19 04:06:07 +0800 CST
Vue3中如何实现状态管理?
2024-11-19 09:40:30 +0800 CST
Go语言中实现RSA加密与解密
2024-11-18 01:49:30 +0800 CST
LLM驱动的强大网络爬虫工具
2024-11-19 07:37:07 +0800 CST
css模拟了MacBook的外观
2024-11-18 14:07:40 +0800 CST
Nginx 防盗链配置
2024-11-19 07:52:58 +0800 CST
Vue3中如何进行错误处理?
2024-11-18 05:17:47 +0800 CST
Vue中的表单处理有哪几种方式?
2024-11-18 01:32:42 +0800 CST
设置mysql支持emoji表情
2024-11-17 04:59:45 +0800 CST
2025年,小程序开发到底多少钱?
2025-01-20 10:59:05 +0800 CST
支付页面html收银台
2025-03-06 14:59:20 +0800 CST
程序员茄子在线接单