別再抱怨后端一次性傳給你 1w 條數(shù)據(jù)了,幾行代碼教會你虛擬滾動

如果后端一次性傳給你 1 萬條數(shù)據(jù),該怎么辦,當然是讓他圓潤的走開,哈哈,開個玩笑。雖然這種情況很少,不過我在實際開發(fā)中還真遇到了類似的情況,接下來我將基于 vue3 實現(xiàn)一個簡單的虛擬滾動。

我們都知道,如果一次性展示所有的數(shù)據(jù),那么會造成頁面卡頓,虛擬滾動的原理就是將數(shù)據(jù)根據(jù)滾動條的位置進行動態(tài)截取,只渲染可視區(qū)域的數(shù)據(jù),這樣瀏覽器的性能就會大大提升,廢話不多說,我們開始。

具體實現(xiàn)

首先,我們先模擬 500 條數(shù)據(jù)

const data = new Array(500).fill(0).map((_, i) => i); // 模擬真實數(shù)據(jù)

然后準備以下幾個容器:

<template>
<div class="view-container">
<div class="content-container"></div>
<div class="item-container">
<div class="item"></div>
</div>
</div>
</template>
  • view-container是展示數(shù)據(jù)的可視區(qū)域,即可滾動的區(qū)域
  • content-container是用來撐起滾動條的區(qū)域,它的高度是實際的數(shù)據(jù)長度乘以每條數(shù)據(jù)的高度,它的作用只是用來撐起滾動條
  • item-container是實際渲染數(shù)據(jù)的區(qū)域
  • item則是具體渲染的數(shù)據(jù)

我們給這幾個容器一點樣式:

.view-container {
height: 400px;
width: 200px;
border: 1px solid red;
overflow-y: scroll;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

.content-container {
height: 1000px;
}

.item-container {
position: absolute;
top: 0;
left: 0;
}

.item {
height: 20px;
}

view-container固定定位并居中,overflow-y設置為scroll;

content-container先給它一個1000px的高度;

item-container絕對定位,top和left都設為 0;

每條數(shù)據(jù)item給他一個20px的高度;

先把 500 條數(shù)據(jù)都渲染上去看看效果:

image.png

這里我們把高度都寫死了,元素的高度是實現(xiàn)虛擬滾動需要用到的變量,因此肯定不能寫死,我們可以用動態(tài)綁定style來給元素加上高度:

首先定義可視高度和每一條數(shù)據(jù)的高度:

const viewHeight = ref(400); // 可視容器高度
const itemHeight = ref(20); // 每一項的高度

用動態(tài)綁定樣式的方式給元素加上高度:

<div class="view-container" :style="{ height: viewHeight + 'px' }">
<div
class="content-container"
:style="{
height: itemHeight * data.length + 'px',
}"
></div>
<div class="item-container">
<div
class="item"
:style="{
height: itemHeight + 'px',
}"
></div>
</div>
</div>

content-container 使用每條數(shù)據(jù)的高度乘以數(shù)據(jù)總長度來得到實際高度。

然后我們定義一個數(shù)組來動態(tài)存放需要展示的數(shù)據(jù),初始展示前 20 條:

const showData = ref<number[]>([]); // 顯示的數(shù)據(jù)
showData.value = data.slice(0, 20); // 初始展示的數(shù)據(jù) (前20個)

showData里的數(shù)據(jù)才是我們要在item遍歷渲染的數(shù)據(jù):

<div
class="item"
:style="{
height: itemHeight + 'px',
}"
v-for="(item, index) in showData"
:key="index"
>
{{ item }}
</div>

接下來我們就可以給view-container添加滾動事件來動態(tài)改變要展示的數(shù)據(jù),具體思路就是:

  1. 根據(jù)滾動的高度除以每一條數(shù)據(jù)的高度得到起始索引
  2. 起始索引加上容器可以展示的條數(shù)得到結束索引
  3. 根據(jù)起始結束索引截取數(shù)據(jù)

具體代碼如下:

const scrollTop = ref(0); // 初始滾動距離
// 滾動事件
const handleScroll = (e: Event) => {
// 獲取滾動距離
scrollTop.value = (e.target as HTMLElement).scrollTop;
// 初始索引 = 滾動距離 / 每一項的高度
const startIndex = Math.round(scrollTop.value / itemHeight.value);
// 結束索引 = 初始索引 + 容器高度 / 每一項的高度
const endIndex = startIndex + viewHeight.value / itemHeight.value;
// 根據(jù)初始索引和結束索引,截取數(shù)據(jù)
showData.value = data.slice(startIndex, endIndex);

console.log(showData.value);
};

打印一下數(shù)據(jù)看看數(shù)據(jù)有沒有改變:

image.png

可以看到數(shù)據(jù)是動態(tài)改變了,但是頁面上卻沒有按照截取的數(shù)據(jù)來展示,這是因為什么呢? 查看一下元素:

image.png

可以看到存放數(shù)據(jù)的元素 也就是 item-container 也跟著向上滾動了,所以我們不要讓它滾動,可以通過調整它的 translateY 的值來實現(xiàn),使其永遠向下偏移滾動條的高度

<div
class="item-container"
:style="{
transform: 'translateY(' + scrollTop + 'px)',
}"
>
<div
class="item"
:style="{
height: itemHeight + 'px',
}"
v-for="(item, index) in showData"
:key="index"
>
{{ item }}
</div>
</div>

看效果:

image.png

文章到此就結束了。這只是一個簡單的實現(xiàn),還有很多可以優(yōu)化的地方,例如滾動太快出現(xiàn)白屏的現(xiàn)象等,大家可以嘗試一下,并試著優(yōu)化一下。

希望本文能夠對你有幫助。

作者:路遙知碼li
鏈接:
https://juejin.cn/post/7301911743487590452

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容