最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3虛擬列表的實(shí)現(xiàn)原理與實(shí)戰(zhàn)指南

 更新時(shí)間:2026年03月20日 10:05:41   作者:寧雨橋  
在日常開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到需要展示大量數(shù)據(jù)的場(chǎng)景,比如展示幾千條甚至上萬(wàn)條數(shù)據(jù)列表,如果直接將所有數(shù)據(jù)渲染到頁(yè)面中,會(huì)導(dǎo)致嚴(yán)重的性能問(wèn)題,虛擬列表正是為了解決這一問(wèn)題而誕生的技術(shù),本文將介紹虛擬列表的核心原理,并基于 Vue3 手寫(xiě)一個(gè)完整的虛擬列表組件

前言

在日常開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到需要展示大量數(shù)據(jù)的場(chǎng)景,比如展示幾千條甚至上萬(wàn)條數(shù)據(jù)列表。如果直接將所有數(shù)據(jù)渲染到頁(yè)面中,會(huì)導(dǎo)致嚴(yán)重的性能問(wèn)題,頁(yè)面會(huì)變得卡頓甚至崩潰。虛擬列表(Virtual List)正是為了解決這一問(wèn)題而誕生的技術(shù)。

本文將介紹虛擬列表的核心原理,并基于 Vue3 手寫(xiě)一個(gè)完整的虛擬列表組件。

一、什么是虛擬列表

虛擬列表是一種按需渲染的技術(shù),它只渲染當(dāng)前可視區(qū)域內(nèi)的列表項(xiàng),而不是一次性渲染所有數(shù)據(jù)。想象一下,你有一本1000頁(yè)的書(shū),但你在任何時(shí)候只能看到其中的一頁(yè)。虛擬列表的工作方式就和翻書(shū)一樣——只展示當(dāng)前"看得到"的內(nèi)容。

虛擬列表的核心優(yōu)勢(shì)

優(yōu)勢(shì)說(shuō)明
性能提升只渲染可見(jiàn)區(qū)域的數(shù)據(jù),DOM 節(jié)點(diǎn)數(shù)量大幅減少
內(nèi)存優(yōu)化避免創(chuàng)建大量 DOM 節(jié)點(diǎn),降低內(nèi)存占用
流暢體驗(yàn)滾動(dòng)更加流暢,不會(huì)因?yàn)閿?shù)據(jù)量大而卡頓
快速首屏首屏渲染時(shí)間大大縮短

二、虛擬列表的實(shí)現(xiàn)原理

虛擬列表的核心思想其實(shí)很簡(jiǎn)單,可以用以下幾個(gè)步驟來(lái)概括:

  1. 計(jì)算可視區(qū)域:根據(jù)滾動(dòng)位置和容器高度,計(jì)算出當(dāng)前可見(jiàn)的數(shù)據(jù)范圍
  2. 按需渲染:只渲染計(jì)算出的可見(jiàn)數(shù)據(jù),而不是全部數(shù)據(jù)
  3. 占位模擬:通過(guò) padding 或 transform 模擬完整的滾動(dòng)高度,讓滾動(dòng)條看起來(lái)正常

下面這張圖清晰地展示了虛擬列表的工作原理:

┌─────────────────────────────┐
│  已渲染區(qū)域(可見(jiàn))           │
│  ┌─────────────────────┐     │
│  │ Item 3              │     │
│  │ Item 4              │     │
│  │ Item 5              │     │ ← 當(dāng)前可見(jiàn)區(qū)域
│  │ Item 6              │     │
│  │ Item 7              │     │
│  └─────────────────────┘     │
├─────────────────────────────┤
│  未渲染區(qū)域(不可見(jiàn))         │
│  ... Item 8-100              │
└─────────────────────────────┘

三、代碼實(shí)現(xiàn)

子組件:MyList.vue

<template>
    <div style="overflow-y:auto ;border:3px solid blue;position: relative;" :style="{ height:props.size * 10 + 'px'}" @scroll="handSrool">
        <ul :style="{ height:(props.size+1) * props.testData.length + 'px', transform: `translateY(${offsetY}px)` }" >
            <li v-for="(item, index) in list" class="list-item" :style="{ height:props.size + 'px', position: 'absolute', top: (index * props.size) + 'px', width: '100%' }">
                {{ item }}
            </li>
        </ul>
    </div>
</template>
<script setup>
import { onMounted, ref, computed } from 'vue';
// 接受父節(jié)點(diǎn)參數(shù)
const props = defineProps({
    testData: Array,
    size: {
        type: Number,
        default: '6'
    }
})
const startIndex = ref(0)
const endIndex = computed(() => startIndex.value + 10)
// 計(jì)算偏移量,讓列表項(xiàng)跟隨滾動(dòng)
const offsetY = computed(() => startIndex.value * props.size)
const list = computed(() => props.testData.slice(startIndex.value, endIndex.value))
// 監(jiān)聽(tīng)滾動(dòng)
const handSrool = (e) => {
    // 獲取滾動(dòng)距離
    const scrollTop = e.target.scrollTop
    // 根據(jù)滾動(dòng)距離計(jì)算起始索引
    startIndex.value = Math.floor(scrollTop / props.size)
}
onMounted(() => {
    console.log(props.size)
    console.log(endIndex.value)
})
</script>
<style>
.list-item {
    font-size: 16px;
    display: flex;
    justify-content: center;
    align-items: center;
    border: 1px solid rgb(146, 144, 144);
}
</style>

父組件:使用示例

<script setup>
import MyList from './components/MyList.vue';
const data = []
// 模擬數(shù)據(jù)
for (let i = 0; i < 100; i++) {
  data.push({
    id: i,
    name: `name${i}`,
    age: i,
    sex: i % 2 === 0 ? '男' : '女',
    address: `address${i}`,
    date: new Date(),
  })
}
</script>
<template>
  <div class="table_continue">
     <MyList :testData="data" :size="60"/>
  </div>
</template>
<style scoped>
.table_continue {
  padding: 20px;
}
</style>

四、核心實(shí)現(xiàn)解析

1. 計(jì)算可見(jiàn)區(qū)域的起始索引

const handSrool = (e) => {
    const scrollTop = e.target.scrollTop
    startIndex.value = Math.floor(scrollTop / props.size)
}

這是虛擬列表最核心的邏輯。當(dāng)用戶(hù)滾動(dòng)時(shí),我們通過(guò) scrollTop 獲取滾動(dòng)距離,然后除以單個(gè)列表項(xiàng)的高度,就能計(jì)算出當(dāng)前應(yīng)該從第幾條數(shù)據(jù)開(kāi)始渲染。

為什么要這樣做?

假設(shè)每個(gè)列表項(xiàng)高度是 60px,滾動(dòng)位置是 300px,那么 300 / 60 = 5,說(shuō)明用戶(hù)已經(jīng)滾過(guò)了5個(gè)列表項(xiàng),所以我們應(yīng)該從第5條數(shù)據(jù)開(kāi)始渲染。

2. 計(jì)算可見(jiàn)數(shù)據(jù)

const list = computed(() => props.testData.slice(startIndex.value, endIndex.value))

使用 slice 方法截取數(shù)組中的一部分,只渲染從 startIndex 開(kāi)始的 10 條數(shù)據(jù)。這樣無(wú)論原數(shù)組有多大,DOM 中最多只有 10 個(gè)列表項(xiàng)元素。

3. 使用 transform 模擬滾動(dòng)

const offsetY = computed(() => startIndex.value * props.size)

這是實(shí)現(xiàn)無(wú)縫滾動(dòng)的關(guān)鍵。雖然我們只渲染了 10 條數(shù)據(jù),但通過(guò) transform: translateY() 將整個(gè)列表向下偏移,偏移量等于被"跳過(guò)"的數(shù)據(jù)的總高度。這樣用戶(hù)看起來(lái)就像在滾動(dòng)整個(gè)列表一樣。

4. 占位容器的高度

:style="{ height:(props.size+1) * props.testData.length + 'px' }"

外層容器的高度等于單個(gè)列表項(xiàng)高度乘以總數(shù)據(jù)條數(shù),這就是讓滾動(dòng)條看起來(lái)正常的"秘密"。瀏覽器會(huì)根據(jù)這個(gè)高度生成相應(yīng)長(zhǎng)度的滾動(dòng)條,用戶(hù)拖動(dòng)滾動(dòng)條時(shí)就會(huì)觸發(fā) scroll 事件。

五、性能優(yōu)化建議

雖然上面的實(shí)現(xiàn)已經(jīng)能夠正常工作,但在生產(chǎn)環(huán)境中,你可能還需要考慮以下優(yōu)化點(diǎn):

1. 預(yù)渲染更多數(shù)據(jù)

可以在可見(jiàn)區(qū)域的前后各多渲染幾條數(shù)據(jù),避免快速滾動(dòng)時(shí)出現(xiàn)空白:

const BUFFER = 3
const endIndex = computed(() => startIndex.value + 10 + BUFFER)
const list = computed(() => {
    const start = Math.max(0, startIndex.value - BUFFER)
    return props.testData.slice(start, endIndex.value)
})

2. 使用防抖處理滾動(dòng)事件

如果數(shù)據(jù)量非常大,可以使用防抖來(lái)減少計(jì)算頻率:

import { debounce } from 'lodash-es'

const handSrool = debounce((e) => {
    const scrollTop = e.target.scrollTop
    startIndex.value = Math.floor(scrollTop / props.size)
}, 16)

3. 處理列表項(xiàng)高度不一致的情況

在實(shí)際應(yīng)用中,列表項(xiàng)的高度可能是動(dòng)態(tài)的。這時(shí)可以使用測(cè)量行高的方案,或者將所有列表項(xiàng)設(shè)為固定高度。

4. 使用 CSS will-change 優(yōu)化動(dòng)畫(huà)

.list-item {
    will-change: transform;
}

這告訴瀏覽器該元素會(huì)發(fā)生變化,可以提前進(jìn)行優(yōu)化。

六、原理圖解

讓我們通過(guò)一張圖來(lái)理解整個(gè)虛擬列表的工作流程:

用戶(hù)滾動(dòng) → 獲取 scrollTop → 計(jì)算 startIndex → slice 取數(shù)據(jù) → translateY 偏移
     │                                              │
     ↓                                              ↓
┌─────────┐    ┌─────────────┐    ┌──────────┐    ┌────────────┐
│ 滾動(dòng)事件 │ →  │ scrollTop   │ →  │ 300/60=5  │ →  │ 取第5-15條  │
│ 觸發(fā)    │    │ = 300px     │    │ start=5   │    │ translateY │
└─────────┘    └─────────────┘    └──────────┘    │ = 300px    │
                                                   └────────────┘

七、總結(jié)

虛擬列表是前端性能優(yōu)化中非常重要的技術(shù)之一。它的核心思想可以用一句話(huà)概括:只渲染可見(jiàn)的內(nèi)容,用占位符模擬完整的滾動(dòng)高度。

通過(guò)本文的實(shí)現(xiàn),我們可以看到一個(gè)基礎(chǔ)的虛擬列表組件并不復(fù)雜,主要涉及到以下幾個(gè)關(guān)鍵點(diǎn):

  1. 監(jiān)聽(tīng)滾動(dòng)事件:獲取用戶(hù)的滾動(dòng)位置
  2. 計(jì)算可見(jiàn)索引:根據(jù)滾動(dòng)位置計(jì)算應(yīng)該顯示的數(shù)據(jù)范圍
  3. 按需渲染:只渲染計(jì)算范圍內(nèi)的數(shù)據(jù)
  4. 模擬滾動(dòng):通過(guò) transform 或 padding 模擬完整的滾動(dòng)高度

掌握了這些核心原理后,你就可以在此基礎(chǔ)上進(jìn)行各種定制化開(kāi)發(fā),比如支持動(dòng)態(tài)高度的列表項(xiàng)、添加加載更多功能、實(shí)現(xiàn)分組虛擬列表等。

以上就是Vue3虛擬列表的實(shí)現(xiàn)原理與實(shí)戰(zhàn)指南的詳細(xì)內(nèi)容,更多關(guān)于Vue3虛擬列表實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于vue2實(shí)現(xiàn)上拉加載功能

    基于vue2實(shí)現(xiàn)上拉加載功能

    這篇文章主要為大家詳細(xì)介紹了基于vue2實(shí)現(xiàn)上拉加載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • vue router返回到指定的路由的場(chǎng)景分析

    vue router返回到指定的路由的場(chǎng)景分析

    這篇文章主要介紹了vue router返回到指定的路由的場(chǎng)景分析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • vue中的mixins混入使用方法

    vue中的mixins混入使用方法

    這篇文章主要介紹了vue中的mixins混入使用方法,混入又分全局混入混入局部混入,下文對(duì)兩者都有相關(guān)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-04-04
  • Vue中Vue-Baidu-Map基本使用方法實(shí)例

    Vue中Vue-Baidu-Map基本使用方法實(shí)例

    最近有一個(gè)項(xiàng)目需要用到地圖來(lái)展示位置并進(jìn)行數(shù)據(jù)交互,用vue-baidu-map實(shí)現(xiàn)出來(lái),下面這篇文章主要給大家介紹了關(guān)于Vue中Vue-Baidu-Map基本使用的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • vue3中如何使用mqtt數(shù)據(jù)傳輸

    vue3中如何使用mqtt數(shù)據(jù)傳輸

    這篇文章主要為大家詳細(xì)介紹了vue3中如何使用mqtt數(shù)據(jù)傳輸,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11
  • vue 實(shí)現(xiàn)錨點(diǎn)功能操作

    vue 實(shí)現(xiàn)錨點(diǎn)功能操作

    這篇文章主要介紹了vue 實(shí)現(xiàn)錨點(diǎn)功能操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • 在Vue3和TypeScript中大文件分片上傳的實(shí)現(xiàn)與優(yōu)化

    在Vue3和TypeScript中大文件分片上傳的實(shí)現(xiàn)與優(yōu)化

    本文介紹在 Vue 3 和 TypeScript 環(huán)境下大文件分片上傳的實(shí)現(xiàn)與優(yōu)化,包括項(xiàng)目前后端技術(shù)棧,前端的文件切片、并發(fā)上傳、計(jì)算 Hash、斷點(diǎn)續(xù)傳和用戶(hù)體驗(yàn)優(yōu)化,后端的文件接收存儲(chǔ)、切片合并、異常處理與日志記錄,還提及遇到的問(wèn)題及解決方案,總結(jié)了此方式的優(yōu)勢(shì)和重要性
    2025-01-01
  • vue中computed和watch的使用實(shí)例代碼解析

    vue中computed和watch的使用實(shí)例代碼解析

    這篇文章主要介紹了vue中computed和watch的綜合運(yùn)用實(shí)例,主要需求是點(diǎn)擊按鈕實(shí)現(xiàn)天氣的切換效果結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • vue如何處理base64格式文件pdf及圖片預(yù)覽功能

    vue如何處理base64格式文件pdf及圖片預(yù)覽功能

    這篇文章主要給大家介紹了關(guān)于vue如何處理base64格式文件pdf及圖片預(yù)覽功能的相關(guān)資料,圖片的base64編碼就是可以將一副圖片數(shù)據(jù)編碼成一串字符串,使用該字符串代替圖像地址,需要的朋友可以參考下
    2024-05-05
  • vue改變對(duì)象或數(shù)組時(shí)的刷新機(jī)制的方法總結(jié)

    vue改變對(duì)象或數(shù)組時(shí)的刷新機(jī)制的方法總結(jié)

    這篇文章主要介紹了vue改變對(duì)象或數(shù)組時(shí)的刷新機(jī)制的方法總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-04-04

最新評(píng)論

叙永县| 名山县| 叶城县| 绥芬河市| 平湖市| 即墨市| 威海市| 胶南市| 泽州县| 桦甸市| 玛沁县| 墨江| 左云县| 库尔勒市| 柳河县| 马边| 镇沅| 柳江县| 河池市| 梨树县| 海南省| 永康市| 邢台市| 竹溪县| 浦东新区| 大埔区| 新郑市| 砀山县| 拉孜县| 舒城县| 临桂县| 新晃| 县级市| 高密市| 兴城市| 河南省| 兴义市| 天津市| 星子县| 自治县| 桑日县|