编程 如何实现虚拟滚动

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

如何实现虚拟滚动

(本文使用 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

推荐文章

Go 开发中的热加载指南
2024-11-18 23:01:27 +0800 CST
Go语言SQL操作实战
2024-11-18 19:30:51 +0800 CST
go错误处理
2024-11-18 18:17:38 +0800 CST
软件定制开发流程
2024-11-19 05:52:28 +0800 CST
Vue3中如何处理组件间的动画?
2024-11-17 04:54:49 +0800 CST
使用 `nohup` 命令的概述及案例
2024-11-18 08:18:36 +0800 CST
一个数字时钟的HTML
2024-11-19 07:46:53 +0800 CST
linux设置开机自启动
2024-11-17 05:09:12 +0800 CST
使用Vue 3实现无刷新数据加载
2024-11-18 17:48:20 +0800 CST
如何在Rust中使用UUID?
2024-11-19 06:10:59 +0800 CST
Nginx rewrite 的用法
2024-11-18 22:59:02 +0800 CST
Go语言中实现RSA加密与解密
2024-11-18 01:49:30 +0800 CST
Linux 网站访问日志分析脚本
2024-11-18 19:58:45 +0800 CST
H5端向App端通信(Uniapp 必会)
2025-02-20 10:32:26 +0800 CST
微信小程序热更新
2024-11-18 15:08:49 +0800 CST
JavaScript设计模式:单例模式
2024-11-18 10:57:41 +0800 CST
Vue3的虚拟DOM是如何提高性能的?
2024-11-18 22:12:20 +0800 CST
php内置函数除法取整和取余数
2024-11-19 10:11:51 +0800 CST
Nginx 性能优化有这篇就够了!
2024-11-19 01:57:41 +0800 CST
html夫妻约定
2024-11-19 01:24:21 +0800 CST
Golang 几种使用 Channel 的错误姿势
2024-11-19 01:42:18 +0800 CST
Go语言中的`Ring`循环链表结构
2024-11-19 00:00:46 +0800 CST
XSS攻击是什么?
2024-11-19 02:10:07 +0800 CST
Rust 并发执行异步操作
2024-11-19 08:16:42 +0800 CST
智慧加水系统
2024-11-19 06:33:36 +0800 CST
程序员茄子在线接单