無論是在真實的項目開發中,還是面試的問題中,對前端的優化處理方式都十分看重,而面對真實的長清單,面對成千上萬的資料清單,要怎麼去渲染呢?
首先最正常的就是前,後端進行約定分頁處理。假如有1000條資料,前端在利用ajax發送一些必要資料後,需要在加上總頁數,和目前頁數,那麼此時,如果約定是10頁的話,那麼現在前端隻是擷取的當頁的10條資料。跳轉不同頁面之後,擷取不同頁面的10條資料。這是最簡單一種處理方式,不過這種方式也有它明顯的弊端,假如要跨頁尋找資料時候,就需要開辟新的存儲空間去存,在實際情況中,甚至存在不能分頁的情況,這時候要怎麼進行優化呢?
先引入一種時間切片的方式,
時間切片的核心思想是:如果存在一個長任務不能在50毫秒内執行完(10萬條資料的加載渲染),那麼為了不阻塞主線程,這個任務應該讓出主線程的控制權,使浏覽器可以處理其他任務。讓出控制權意味着停止執行目前任務,讓浏覽器去執行其他任務,随後再回來繼續執行沒有執行完的任務。
//需要插入的容器
let ul = document.getElementById('container');
// 插入十萬條資料
let total = 100000;
// 一次插入 20 條
let once = 20;
//總頁數
let page = total/once
//每條記錄的索引
let index = 0;
//循環加載資料
function loop(curTotal,curIndex){
if(curTotal <= 0){
return false;
}
//每頁多少條
let pageCount = Math.min(curTotal , once);
setTimeout(()=>{
for(let i = 0; i < pageCount; i++){
let li = document.createElement('li');
li.innerText = curIndex + i + ' : ' + ~~(Math.random() * total)
ul.appendChild(li)
}
loop(curTotal - pageCount,curIndex + pageCount)
},0)
}
loop(total,index);
如上述代碼所示,我們一次利用setTimeout加載20條,每次setTimeout是一次宏任務,在浏覽器的執行機制中,沒進行一輪的宏任務之後會進行渲染,這樣的話,就可以實作,每加載20條資料後渲染,不會造成主線程一直被占用的情況。
第二種方式是虛拟清單
這種方法的核心思想就是,現将所有的資料得到,但是隻去渲染視窗可見部分的資料。話不多說,上圖
這一共有1000條資料,我們一次隻渲染其中可見的10條。如圖現在顯示0 - 9, 随着滾輪往下移動,我們去渲染當時螢幕可以看見的那10條資料。也就是說同一個時間内也隻是顯示10條資料進而減少了對dom的操作,優化了性能。
來分析一下代碼
<!DOCTYPE html>
<html>
<head >
<meta charset="UTF-8">
<title></title>
<style type="text/css" rel="stylesheet">
//分析css部分
.content{
background-color: orangered;
overflow:auto;
//這裡是為了出現滾輪
width: 50vw;
height: 500px;
//一條資料50px高,可視視窗500px,一頁可以放10條資料
margin-left: 25vw;
}
.target{
height: 50000px;
//整個滑輪可以滾動的長短也就是可以存儲1000條資料
box-sizing: border-box;
//這裡需要調整一下盒子模型,後續要配合padding-top屬性設定偏移量。
}
</style>
</head>
<body>
<div class="content" >
<!-- div标簽是可視視窗 -->
<ul class ="target">
<!-- ul标簽是滑動總長度 -->
<!-- li标簽放入其中,用于存放單挑資料 -->
</ul>
</div>
</body>
<script>
let datalength = 10;
//每頁顯示資料
let data = [];
//總共的1000條資料,數組存放
let showdata = [];
//要去渲染的10條資料,數組存放
let topdistance = 0;
//記錄偏移量
let first;
//首條資料的索引
let end;
//尾條資料索引
for(let i = 0;i<1000;i++){
data.push(i);
}
let div = document.querySelector('.content');
let ul = document.querySelector('ul');
myscroll()
//首屏渲染資料
div.onscroll = myscroll
function myscroll(e){
ul.innerHTML = '';
//每次渲染之前清空内容
topdistance = e?e.target.scrollTop:0;
//手動設定偏移量
ul.style.paddingTop = topdistance+'px';
//将偏移量指派給paddingTop,注意上述css盒模型如果不設定一下的話,會導緻滾輪一直下滑,ul的高度會被無限拉長
let index = Math.floor(topdistance/50);
//根據偏移量判斷該從哪裡截取
first = index;
end =index+datalength;
showdata = data.slice(first,end);
//對原生資料進行截取
const dom = document.createDocumentFragment();
let li = null;
//下面操作dom節點進行渲染
showdata.map((item)=>{
li = document.createElement('li');
li.style.height = '50px';
li.innerHTML = item;
dom.appendChild(li);
})
ul.appendChild(dom);
}
</script>
</html>