likes
comments
collection
share

加载慢如蜗牛?试试虚拟列表吧!本文介绍了一种优化Web应用性能的技术——虚拟列表。通过只渲染当前可视区域的数据项,虚拟列

作者站长头像
站长
· 阅读数 33

引言

随着Web应用程序的发展,用户对数据展示的需求日益增加。在一些场景下,我们需要在一个页面中展示成千上万条数据记录,如消息列表、商品列表等。传统的做法是将所有数据一次性加载到页面中,但这会导致页面加载速度缓慢,用户体验不佳。为了解决这个问题,虚拟列表(Virtual Scrolling)技术应运而生。本文将详细介绍虚拟列表的概念、实现原理及其在Vue.js中的具体实现。

什么是虚拟列表?

虚拟列表是一种优化技术,用于在Web应用中高效地渲染大量数据。它的核心思想是只渲染当前可视区域的数据项,而不是一次性渲染整个数据集。这样可以显著减少内存消耗和提高页面性能。

虚拟列表的工作原理

虚拟列表通过以下几个步骤来提高列表的渲染效率:

  1. 计算可视区域:确定当前滚动到的位置以及可视区域的高度。
  2. 确定需要渲染的数据范围:基于可视区域的高度和每项数据的高度,计算出需要显示的数据项范围。
  3. 动态调整渲染范围:随着用户的滚动操作,动态更新需要渲染的数据项范围。
  4. 重用元素:如果可能的话,重用已经创建的DOM节点,而不是每次都创建新的节点。

加载慢如蜗牛?试试虚拟列表吧!本文介绍了一种优化Web应用性能的技术——虚拟列表。通过只渲染当前可视区域的数据项,虚拟列

Vue.js 中的虚拟列表实现

下面我们将详细介绍上述代码是如何实现虚拟列表功能的:

HTML 结构
<template>
    <div ref="listRef" class="infinte-list-container" @scroll="scrollHandle">
      <div class="empty" :style="{height: itemSize * listData.length + 'px'}"></div>
      <div class="infinte-list" :style="{transform: getTransform}">
        <div 
          class="infinte-list-item"
          v-for="item in visibleData"
          :key="item.id"
          :style="{height: itemSize + 'px', lineHeight: itemSize + 'px'}"
        >
          {{item.value}}
        </div>
      </div>
    </div>
</template>
  • listRef 是对包含列表的div元素的引用,通过它可以获取到滚动事件。
  • class="infinte-list-container" 是包含列表的容器,它具有滚动条。
  • class="empty" 是一个占位符,它的高度等于所有数据项的高度总和,确保列表的总高度正确。
  • class="infinte-list" 包含实际要显示的数据项,使用transform来模拟滚动效果。
  • v-for 指令用于遍历visibleData数组,动态渲染数据项。
JavaScript 逻辑
<script setup>
import { ref } from 'vue';
import { computed } from 'vue';
import { reactive, onMounted } from 'vue';

const props = defineProps({
listData: {
  type: Array,
  default: () => []
},
itemSize: {
  type: Number,
  default: 50
}
})

const state = reactive({
scrollHeight: 0,
start: 0,
end: 0,
listOffset: 0
})

// 可视区域能展示几条
const visibleCount = computed(() => {
return Math.ceil(state.scrollHeight / props.itemSize)
})

// 可视区域要展示的数据
const visibleData = computed(() => {
return props.listData.slice(state.start, Math.min(state.end, props.listData.length))
})

// 列表被带出去后移回
const getTransform = computed(() => {
return `translateY(${state.listOffset}px)`
})

const listRef = ref(null)
onMounted(() => {
state.scrollHeight = listRef.value.clientHeight
state.end = state.start + visibleCount.value
})

const scrollHandle = () => {
let scrollTop = listRef.value.scrollTop
state.start = Math.floor(scrollTop / props.itemSize)
state.end = state.start + visibleCount.value
state.listOffset = scrollTop - (scrollTop % props.itemSize)
// console.log(scrollTop);
// 实时计算 start 和 end
}
</script>
  • listData 是传入组件的数据数组。
  • itemSize 是每个数据项的高度。
  • state 存储组件的状态,如可视区域的高度、开始和结束索引等。
  • visibleCount 计算可视区域内可以展示的数据项的数量。
  • visibleData 根据可视区域的起始和结束索引,截取需要显示的数据项。
  • getTransform 计算列表的translateY值,用于模拟滚动效果。
  • listRef 用于引用 DOM 元素。在组件挂载完成后,确保组件正确初始化。
  • scrollHandle 监听滚动事件,更新状态。
CSS 样式
<style lang="css" scoped>
.infinte-list-container{
height: 100%;
overflow: auto;
position: relative;
}
.infinte-list{
position: absolute;
left: 0;
right: 0;
top: 0;
}
.infinte-list-item{
text-align: center;
border-bottom: 1px solid #eee;
box-sizing: border-box;
}
</style>
  • .infinte-list-container 定义了包含列表的容器样式,包括高度、滚动条和相对定位。
  • .infinte-list 定义了列表本身的位置样式,绝对定位以便使用transform
  • .infinte-list-item 定义了每个列表项的样式,包括居中文本、底部边框等。

下面请看实例演示(最终就是如图效果):

加载慢如蜗牛?试试虚拟列表吧!本文介绍了一种优化Web应用性能的技术——虚拟列表。通过只渲染当前可视区域的数据项,虚拟列

总结

通过以上介绍,我们可以看到虚拟列表技术通过只渲染当前可视区域的数据项,极大地提高了列表的渲染性能。Vue.js 提供了强大的响应式系统和生命周期钩子,使得实现虚拟列表变得更加简单和直观。虚拟列表不仅适用于列表,还可以应用于表格、卡片等多种UI组件中,是一个值得掌握的技术。

转载自:https://juejin.cn/post/7418412937607626788
评论
请登录