前端如何一次性渲染10w条数据还能保证页面不卡顿

時小白

关注

阅读 14

2024-10-09

一、 问题分析

  1. 设计不合理的
    1. 后端一次性给到前端大量的数据,本身技术方案设计就不合理
    2. 应该主动沟通,避免不合理的技术方案

二、 方案选择

  1. 如果条件允许,前端可以自定义中间层
    1. 自定义nodejs中间层,获取并拆分这10w条数据
    2. 前端对接nodejs中间层,而不是服务端
    3. 成本比较高
  2. 可以使用虚拟列表
    1. 只渲染可视区域DOM
    2. 其它隐藏区域不显示,只用 div 撑起高度
    3. 随着浏览器滚动,创建和销毁DOM
  3. 虚拟列表 - 第三方lib
    1. 虚拟列表实现起来非常复杂,可借用第三方lib,比如
    2. Vue-virtual-scroll-list
    3. React-virtualiszed
  4. 如果,一定要直接渲染,浏览器能否处理10w条数据
    1. JS计算处理没问题
    2. 渲染到DOM会非常卡顿

三、如果一定要,直接渲染到浏览器,怎么解决渲染卡顿问题:

  1. 直接全部循环渲染,整个页面会卡住,渲染完成后恢复,卡住的时间取决于电脑配置(数据量大或者每条数据,也可能会导致浏览器直接崩溃)

    const total = 100000;
    let ul = document.getElementById('container');
    let num = 0;
    
    for (let i = 0; i < total; i++) {
      num++;
      let li = document.createElement('li');
      li.innerHTML = num + '-' + Date.now();
      ul.appendChild(li);
    }
    
  2. 使用setTimeout把全部一起渲染拆分成每次渲染20条,这样不会因为数据量巨大而崩溃,当然如果电脑配置低,还是会出现卡顿。

    const total = 100000;
    
    let ul = document.getElementById('container');
    let once = 20;
    let num = 0;
    
    function loop(curTotal) {
    if (curTotal <= 0) return;
    
        let pageCount = Math.min(curTotal, once);
    
        setTimeout(() => {
          for (let i = 0; i < pageCount; i++) {
            num++;
            let li = document.createElement('li');
            li.innerHTML = num + '-' + Date.now();
            ul.appendChild(li);
          }
          loop(curTotal - pageCount);
        });
    
    }
    
    loop(total);
    
  3. 进一步优化,还是拆成20条每次,把setTimeout换成requestAnimationFrame,这样,在渲染的过程中,不会有明显的卡顿。

    1. setTimeout任务执行的时间是写死的,requestAnimationFrame执行的时机是与屏幕刷新率同步的
    const total = 100000;
    
    let ul = document.getElementById('container');
    let once = 20;
    let num = 0;
    
    function loop(curTotal) {
    if (curTotal <= 0) return;
    
        let pageCount = Math.min(curTotal, once);
    
        window.requestAnimationFrame(() => {
          for (let i = 0; i < pageCount; i++) {
            num++;
            let li = document.createElement('li');
            li.innerHTML = num + '-' + Date.now();
            ul.appendChild(li);
          }
          loop(curTotal - pageCount);
        });
    
    }
    
    loop(total);
    
  4. 进一步优化性能,把每条数据都执行插入DOM改成每条插入到fragment,每次fragment才插入DOM,那样可以把文档回流的次数减少到1/20

    const total = 100000;
    let ul = document.getElementById('container');
    let once = 20;
    let num = 0;
    
    function loop(curTotal) {
      if (curTotal <= 0) return;
    
      let pageCount = Math.min(curTotal, once);
    
      window.requestAnimationFrame(() => {
        let fragment = document.createDocumentFragment(); // 创建一个虚拟文档碎片
        for (let i = 0; i < pageCount; i++) {
          num++;
          let li = document.createElement('li');
          li.innerHTML = num + '-' + Date.now();
          fragment.appendChild(li); // 挂到fragment上
        }
        ul.appendChild(fragment); // 现在才回流
        loop(curTotal - pageCount);
      });
    }
    
    loop(total);
    

精彩评论(0)

0 0 举报