天天看點

長清單渲染中的虛拟清單處理(内附代碼)

無論是在真實的項目開發中,還是面試的問題中,對前端的優化處理方式都十分看重,而面對真實的長清單,面對成千上萬的資料清單,要怎麼去渲染呢?

首先最正常的就是前,後端進行約定分頁處理。假如有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>
           

長清單渲染中的虛拟清單處理(内附代碼) - 掘金

繼續閱讀