0
点赞
收藏
分享

微信扫一扫

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

一、 问题分析

  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 条评论