前端虛擬列表實(shí)現(xiàn)的思路及完整代碼
一、什么是虛擬列表
虛擬列表(Virtual List)是一種用于優(yōu)化長(zhǎng)列表性能的技術(shù)解決方案。其核心思想是:只渲染可視區(qū)域的列表項(xiàng),而不是渲染整個(gè)列表的所有項(xiàng)。這種技術(shù)特別適用于需要展示大量數(shù)據(jù)的場(chǎng)景,可以顯著提升頁(yè)面性能和用戶體驗(yàn)。
二、為什么需要虛擬列表
假設(shè)我們需要渲染一個(gè)包含10000條數(shù)據(jù)的列表:
性能問(wèn)題:
- DOM節(jié)點(diǎn)過(guò)多會(huì)導(dǎo)致頁(yè)面渲染慢
- 內(nèi)存占用大
- 滾動(dòng)卡頓
用戶體驗(yàn):
- 首屏加載時(shí)間長(zhǎng)
- 操作響應(yīng)慢
- 設(shè)備發(fā)熱嚴(yán)重
三、虛擬列表的實(shí)現(xiàn)原理
1. 核心概念
- 可視區(qū)域(Viewport):用戶能看到的列表區(qū)域
- 可視區(qū)域高度:視口的高度
- 列表項(xiàng)高度:每個(gè)列表項(xiàng)的高度
- 可視區(qū)域起始索引:當(dāng)前可見(jiàn)區(qū)域的第一個(gè)列表項(xiàng)的索引
- 可視區(qū)域結(jié)束索引:當(dāng)前可見(jiàn)區(qū)域的最后一個(gè)列表項(xiàng)的索引
- 偏移量(offset):列表滾動(dòng)的距離
2. 計(jì)算公式
// 可顯示的列表項(xiàng)數(shù)量 visibleCount = Math.ceil(viewportHeight / itemHeight) // 起始索引 startIndex = Math.floor(scrollTop / itemHeight) // 結(jié)束索引 endIndex = startIndex + visibleCount // 偏移量 offset = scrollTop - (scrollTop % itemHeight)
四、代碼實(shí)現(xiàn)
1. 基礎(chǔ)結(jié)構(gòu)
<div class="viewport" @scroll="handleScroll">
<div class="scroll-list" :style="{ height: totalHeight + 'px' }">
<div class="scroll-content" :style="{ transform: `translateY(${offset}px)` }">
<!-- 渲染區(qū)域列表項(xiàng) -->
</div>
</div>
</div>
2. 完整實(shí)現(xiàn)
class VirtualList {
constructor(options) {
this.itemHeight = options.itemHeight;
this.total = options.total;
this.viewport = options.viewport;
this.buffer = options.buffer || 5;
this.viewportHeight = this.viewport.clientHeight;
this.visibleCount = Math.ceil(this.viewportHeight / this.itemHeight);
this.startIndex = 0;
this.endIndex = this.startIndex + this.visibleCount + this.buffer;
this.offset = 0;
this.initDom();
this.bindEvents();
}
initDom() {
this.scrollList = document.createElement('div');
this.scrollList.className = 'scroll-list';
this.scrollList.style.height = this.total * this.itemHeight + 'px';
this.scrollContent = document.createElement('div');
this.scrollContent.className = 'scroll-content';
this.scrollList.appendChild(this.scrollContent);
this.viewport.appendChild(this.scrollList);
}
bindEvents() {
this.viewport.addEventListener('scroll', this.handleScroll.bind(this));
}
handleScroll() {
const scrollTop = this.viewport.scrollTop;
this.startIndex = Math.floor(scrollTop / this.itemHeight);
this.endIndex = this.startIndex + this.visibleCount + this.buffer;
this.offset = this.startIndex * this.itemHeight;
this.updateContent();
}
updateContent() {
const visibleData = this.getVisibleData();
this.scrollContent.style.transform = `translateY(${this.offset}px)`;
// 更新列表內(nèi)容
this.renderData(visibleData);
}
getVisibleData() {
return this.data.slice(this.startIndex, this.endIndex);
}
renderData(data) {
// 根據(jù)數(shù)據(jù)渲染DOM
}
}
五、優(yōu)化技巧
1. 使用 Buffer 緩沖區(qū)
在可視區(qū)域的上下額外渲染一些列表項(xiàng),可以改善快速滾動(dòng)時(shí)的白屏問(wèn)題:
const bufferSize = 5; startIndex = Math.max(0, startIndex - bufferSize); endIndex = Math.min(total, endIndex + bufferSize);
2. 動(dòng)態(tài)高度處理
對(duì)于列表項(xiàng)高度不固定的情況,可以:
- 預(yù)估高度
- 緩存真實(shí)高度
- 動(dòng)態(tài)更新位置
class DynamicVirtualList {
constructor() {
this.heightCache = new Map();
this.estimatedHeight = 100;
}
updatePosition() {
let totalHeight = 0;
for (let i = 0; i < this.startIndex; i++) {
totalHeight += this.getItemHeight(i);
}
this.offset = totalHeight;
}
getItemHeight(index) {
return this.heightCache.get(index) || this.estimatedHeight;
}
}
3. 防抖和節(jié)流
對(duì)滾動(dòng)事件進(jìn)行防抖和節(jié)流處理,避免頻繁計(jì)算:
function throttle(fn, delay) {
let timer = null;
return function() {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}
}
handleScroll = throttle(function() {
// 滾動(dòng)處理邏輯
}, 16);
六、性能優(yōu)化建議
使用 transform 代替 top:
- transform 執(zhí)行在合成層,性能更好
- 避免重排
RAF優(yōu)化:
requestAnimationFrame(() => { this.updateContent(); });列表項(xiàng)組件緩存:
- 使用React.memo或Vue的keep-alive
- 避免不必要的重渲染
避免在滾動(dòng)時(shí)進(jìn)行大量計(jì)算:
- 預(yù)計(jì)算數(shù)據(jù)
- 使用Web Worker
七、使用場(chǎng)景
- 長(zhǎng)列表渲染
- 無(wú)限滾動(dòng)
- 大數(shù)據(jù)表格
- 時(shí)間軸
- 聊天記錄
八、注意事項(xiàng)
- 需要準(zhǔn)確設(shè)置容器高度
- 處理列表項(xiàng)動(dòng)態(tài)高度
- 考慮滾動(dòng)到底部的加載更多
- 保持滾動(dòng)位置
- 鍵盤訪問(wèn)性
這篇文章詳細(xì)介紹了虛擬列表的概念、實(shí)現(xiàn)原理和優(yōu)化方案。主要包括:
- 基本概念和原理解釋
- 完整的代碼實(shí)現(xiàn)
- 多種優(yōu)化方案
- 實(shí)際應(yīng)用場(chǎng)景
- 注意事項(xiàng)
總結(jié)
到此這篇關(guān)于前端虛擬列表實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)前端虛擬列表實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中使用router全局守衛(wèi)實(shí)現(xiàn)頁(yè)面攔截的示例
這篇文章主要介紹了vue中使用router全局守衛(wèi)實(shí)現(xiàn)頁(yè)面攔截的示例,幫助大家維護(hù)自己的項(xiàng)目,感興趣的朋友可以了解下2020-10-10
Vue項(xiàng)目利用axios請(qǐng)求接口下載excel
這篇文章主要為大家詳細(xì)介紹了Vue項(xiàng)目利用axios請(qǐng)求接口下載excel,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
vue-cli2.x舊版本卸載不掉的問(wèn)題踩坑指南(附Vue腳手架安裝教程)
遇到一個(gè)Vuecli2腳手架卸載不了的問(wèn)題,查了許多資料說(shuō)的都比較復(fù)雜,所以下面這篇文章主要給大家介紹了關(guān)于vue-cli2.x舊版本卸載不掉的問(wèn)題踩坑的相關(guān)資料,文中還附了Vue腳手架安裝教程,需要的朋友可以參考下2022-07-07
詳細(xì)解釋Vue3中的getCurrentInstance是什么
這篇文章主要介紹了Vue3中g(shù)etCurrentInstance是什么的相關(guān)資料,getCurrentInstance是Vue3提供的一個(gè)API,用于在組合式API中獲取當(dāng)前組件實(shí)例,從而訪問(wèn)組件的屬性、方法、插槽等信息,文中將用法介紹的非常詳細(xì),需要的朋友可以參考下2025-05-05
elementPlus?的el-select在提示框關(guān)閉時(shí)自動(dòng)彈出的問(wèn)題解決
這篇文章主要介紹了elementPlus?的el-select在提示框關(guān)閉時(shí)自動(dòng)彈出閉時(shí)自動(dòng)彈出的問(wèn)題,主要問(wèn)題就是因?yàn)閒ilterable屬性,根本解決方案是選中的時(shí)候讓他失去焦點(diǎn)?el-select有一個(gè)visible-change事件,下拉框出現(xiàn)/隱藏時(shí)觸發(fā),感興趣的朋友跟隨小編一起看看吧2024-01-01
Element input樹(shù)型下拉框的實(shí)現(xiàn)代碼
這篇文章主要介紹了Element input樹(shù)型下拉框的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
moment轉(zhuǎn)化時(shí)間戳出現(xiàn)Invalid Date的問(wèn)題及解決
這篇文章主要介紹了moment轉(zhuǎn)化時(shí)間戳出現(xiàn)Invalid Date的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05

