引言
隨著Web應用程式的發展,使用者對資料展示的需求日益增加。在一些場景下,我們需要在一個頁面中展示成千上萬條資料記錄,如訊息列表、商品列表等。傳統的做法是將所有資料一次性載入到頁面中,但這會導致頁面載入速度緩慢,使用者體驗不佳。爲了解決這個問題,虛擬列表(Virtual Scrolling)技術應運而生。本文將詳細介紹虛擬列表的概念、實現原理及其在Vue.js中的具體實現。
什麼是虛擬列表?
虛擬列表是一種最佳化技術,用於在Web應用中高效地渲染大量資料。它的核心思想是隻渲染當前可視區域的資料項,而不是一次性渲染整個資料集。這樣可以顯著減少記憶體消耗和提高頁面效能。
虛擬列表的工作原理
虛擬列表透過以下幾個步驟來提高列表的渲染效率:
計算可視區域:確定當前滾動到的位置以及可視區域的高度。
確定需要渲染的資料範圍:基於可視區域的高度和每項資料的高度,計算出需要顯示的資料項範圍。
動態調整渲染範圍:隨著使用者的滾動操作,動態更新需要渲染的資料項範圍。
重用元素:如果可能的話,重用已經建立的DOM節點,而不是每次都建立新的節點。
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 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
定義了每個列表項的樣式,包括居中文字、底部邊框等。
下面請看例項演示(最終就是如圖效果):
總結
透過以上介紹,我們可以看到虛擬列表技術透過只渲染當前可視區域的資料項,極大地提高了列表的渲染效能。Vue.js 提供了強大的響應式系統和生命週期鉤子,使得實現虛擬列表變得更加簡單和直觀。虛擬列表不僅適用於列表,還可以應用於表格、卡片等多種UI元件中,是一個值得掌握的技術。