前端渲染十万条数据的实现方案与优化技巧

前端高效渲染十万条数据的实现方案与优化技巧

  • 一、时间分片
  • 二、虚拟列表
  • 三、IntersectionObserver api
  • 四、虚拟DOM优化

一、时间分片

时间分片是一种前端性能优化策略,通过将大量数据的渲染任务拆分成多个小任务(分片),在浏览器的每一帧中逐步执行,避免长时间阻塞主线程,从而保证页面的流畅交互。
实现原理:

  • 分块处理机制:将完整数据集按固定大小分割为多个数据块(如100条/块),每个事件循环周期仅处理单个数据块的内容
  • 异步任务调度:使用setTimeout将后续处理任务放入事件队列。即使设置0ms延迟也会让出当前主线程控制权
  • 渐进式渲染:每个分片处理完成后浏览器可获得执行渲染的机会,实现边加载边显示的效果

1.使用 setTimeout 分片

function renderData(data, chunkSize = 100) {
  let index = 0;
  function processChunk() {
    const chunk = data.slice(index, index + chunkSize);
    chunk.forEach(item => {
      const div = document.createElement('div');
      div.textContent = item;
      document.body.appendChild(div);
    });
    index += chunkSize;
    if (index < data.length) {
      setTimeout(processChunk, 0); // 下一事件循环继续渲染
    }
  }
  processChunk();
}

2.使用 requestAnimationFrame(更流畅)

function renderWithRAF(data, chunkSize = 100) {
  let index = 0;
  function processChunk() {
    const startTime = performance.now();
    while (index < data.length && performance.now() - startTime < 16) {
      // 每帧最多执行 16ms(60fps 标准)
      const item = data[index];
      const div = document.createElement('div');
      div.textContent = item;
      document.body.appendChild(div);
      index++;
    }
    if (index < data.length) {
      requestAnimationFrame(processChunk);
    }
  }
  processChunk();
}

3.使用 requestIdleCallback(后台低优先级渲染),用于浏览器的空闲时间执行非紧急的、对实时性要求不高的渲染任务

function renderWithIdle(data, chunkSize = 100) {
  let index = 0;
  function processChunk(deadline) {
    while (index < data.length && deadline.timeRemaining() > 0) {
      const item = data[index];
      const div = document.createElement('div');
      div.textContent = item;
      document.body.appendChild(div);
      index++;
    }
    if (index < data.length) {
      requestIdleCallback(processChunk); // 浏览器空闲时继续
    }
  }
  requestIdleCallback(processChunk);
}

二、虚拟列表

虚拟列表(Virtual List)是一种优化长列表渲染性能的前端技术,渲染当前可视区域内的列表项,而不是渲染整个列表。这样可以显著减少DOM节点的数量,提高性能。
实现原理:
1.可视区域计算:确定容器的可视高度,计算当前滚动位置,确定可见项的起始和结束索引
2.动态渲染:只渲染当前可见的列表项。使用空白占位元素(padding)模拟完整列表的高度
3.滚动处理:监听滚动事件,滚动时动态更新可见项




    
    
    虚拟列表示例 - 十万条数据
    


虚拟列表示例 - 十万条数据

总数据量: 0

实际渲染DOM: 0

当前可见范围: 0-0

三、IntersectionObserver api

IntersectionObserver API 实现"加载更多"功能的核心原理是通过观察一个"哨兵元素"(sentinel)与视口的交叉状态,当这个元素进入视口时触发数据加载。
实现原理:
1.哨兵元素设置:在内容列表的最后放置一个特殊的空元素,这个元素没有高度或很小高度,仅作为观察目标
2.触发条件:当用户滚动页面,哨兵元素进入视口时,IntersectionObserver 检测到交叉状态变化,触发回调函数执行加载逻辑
3.加载过程:在回调中检查 entry.isIntersecting 是否为 true,如果是,则发起数据请求,获取新数据后追加到列表末尾,哨兵元素自动被推到新内容的最后,等待下次触发




    
    
    十万条数据加载示例
    


大数据列表加载

加载中...

四、虚拟DOM优化

当需要向文档中添加多个节点时,如果直接逐个添加,每次操作都会引起页面的重新渲染(回流和重绘),导致性能消耗较大。使用document.createDocumentFragment()创建虚拟dom可以减少页面渲染的次数,从而提高性能。因为文档片段存在于内存中,对其操作不会立即触发页面的重新渲染。

function renderData(data, chunkSize = 100) {
  let index = 0;
  function processChunk() {
    const fragment = document.createDocumentFragment(); // 创建文档片段
    const chunk = data.slice(index, index + chunkSize);
    chunk.forEach(item => {
      const div = document.createElement('div');
      div.textContent = item;
      fragment.appendChild(div); // 先将元素添加到文档片段
    });
    document.body.appendChild(fragment); // 一次性将片段添加到DOM
    index += chunkSize;
    if (index < data.length) {
      setTimeout(processChunk, 0);
    }
  }
  processChunk();
}

你可能感兴趣的:(前端)