High-performance rendering of 100,000 pieces of data: time slicing, virtual list, lazy loading

Render large amounts of data with high performance

For the rendering of large amounts of data, time slicing and virtual list processing can be used

time slicing

A simple understanding is to split the data into many parts and render them in batches. Page jank is due to time-slicing available due to rendering a large number of DOMs at the same time.

Method 1 : setTimeout
Disadvantages : But when we scroll the page quickly, we will find that the page has a splash screen or a white screen

//需要插入的容器
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);

Method 2 :requestAnimationFrame + DocumentFragment

//需要插入的容器
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);
    window.requestAnimationFrame(function(){
    
    
        let fragment = document.createDocumentFragment();
        for(let i = 0; i < pageCount; i++){
    
    
            let li = document.createElement('li');
            li.innerText = curIndex + i + ' : ' + ~~(Math.random() * total)
            fragment.appendChild(li)
        }
        ul.appendChild(fragment)
        loop(curTotal - pageCount,curIndex + pageCount)
    })
}
loop(total,index);

virtual list

The realization of the virtual list is actually to load only the list items required in the visible area when the first screen is loaded. When scrolling occurs, the list items in the visible area are dynamically obtained through calculation, and the non-visible area memory The list item in is removed.

1. Calculate the start data index of the current visible area (startIndex)
2. Calculate the end data index of the current visible area (endIndex)
3. Calculate the data of the current visible area and render it to the page
4. Calculate the data corresponding to startIndex in The offset position startOffset in the entire list and set to the list
insert image description here

<!-- 虚拟列表 -->
  <div class="box">
    <!-- 内容真实高度 即200条数据的高度-->
    <div class="content-area" id="contentArea">
      <ul></ul>
    </div>
  </div>
	let el = document.querySelector(".box");
    let itemHeight = 110; //每个元素的高度(li的高度100 + marginBottom 10)
    let pageSize = Math.ceil(el.clientHeight / itemHeight); // 获取一个滚动屏最大可容纳子元素个数(向上取整)
    let data = []; //mock数据
    let startIndex = 0; //可视区第一行下标
    let endIndex = pageSize; //可视区最后一行下标

    // 初始化模拟数据
    let getData = () => {
    
    
      for (let i = 0; i < 200; i++) {
    
    
        data.push({
    
    
          content: `我是显示的内容${
      
      i + 1}`,
        });
      }
    };

    // 加载数据并插入到dom页面
    let loadData = () => {
    
    
      let html = "";
      let sliceData = data.slice(startIndex, endIndex);
      for (let i = 0; i < sliceData.length; i++) {
    
    
        html += `
            <li class="item">
              <p>${
      
      sliceData[i].content}</p>
            </li>`;
      }
      el.querySelector("#contentArea ul").innerHTML = html;
    };

    // 更新DOM
    let updateHtml = () => {
    
    
      let sliceData = data.slice(startIndex, endIndex);
      let itemAll = el.querySelectorAll(".item");
      for (let i = 0; i < sliceData.length; i++) {
    
    
        itemAll[i].querySelector("p").innerHTML = sliceData[i].content;
      }
    };

    // 滑动监听
    el.addEventListener("scroll", function () {
    
    
      let scrollTop = el.scrollTop; // 滚动高度
      startIndex = Math.ceil(scrollTop / itemHeight); // 重新计算开始的下标,div顶部卷起来的长度除以列表元素的高度
      endIndex = startIndex + pageSize;
      updateHtml(); // 重新更新dom
      el.querySelector("#contentArea ul").style.transform =
        "translateY(" + startIndex * itemHeight + "px)";
    });

    let init = () => {
    
    
      getData();
      loadData();
      document.getElementById("contentArea").style.height =
        itemHeight * data.length + "px"; // 占位dom的高度
    };

    // 页面初始化调用
    init();

lazy loading

Not much introduction, one sentence explanation: At the beginning, all data is not rendered, only the data visible on the view is displayed, when scrolling to the bottom of the page, more data is loaded

Implementation principle: By listening to the scroll event of the parent element, of course, it can also be implemented through APIs such as IntersectionObserver or getBoundingClientRect

However, the scroll event will be triggered frequently, so you need to manually throttle; there are a lot of DOM in the scrolling element, which is easy to cause freezes, it is recommended to use IntersectionObserver

Guess you like

Origin blog.csdn.net/qq_38110274/article/details/127527148