前端性能救星!用 requestAnimationFrame 丝滑渲染海量数据

大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。

我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。

技术qq交流群:906392632

大家好,我是小杨,一个做了6年前端的老司机。今天想和大家聊聊一个常见但头疼的问题:如何在不卡死页面的情况下渲染大量数据?

不知道你有没有遇到过这样的场景:后端一次性返回几万条数据,前端直接 forEach + appendChild 暴力渲染,结果页面直接卡死,用户疯狂投诉......(别问我怎么知道的 😅)

其实,解决这个问题并不难,关键在于分批渲染 ,而 requestAnimationFrame(简称 rAF)就是我们的最佳帮手!


1. 为什么直接渲染大数据会卡?

假设我们有 10,000 条数据,如果一次性全部塞进 DOM:

javascript 复制代码
const data = Array(10000).fill("我是数据项");  
const container = document.getElementById('list');  

data.forEach(item => {
  const div = document.createElement('div');
  div.textContent = item;
  container.appendChild(div);
});

问题来了:

  • JS 执行阻塞:一次性创建 10,000 个 DOM 节点,JS 引擎会卡住主线程。
  • 渲染延迟:浏览器需要处理大量 DOM 插入,导致页面冻结,用户无法交互。
  • 内存占用高:大量 DOM 节点堆积,内存飙升,甚至可能崩溃。

结论: 不能一次性渲染所有数据!


2. 解决方案:分批渲染 + requestAnimationFrame

requestAnimationFrame 是浏览器提供的 API,它会在下一次重绘之前 执行回调,通常是 16.6ms(60FPS) 执行一次,这样可以让渲染更流畅。

优化思路:

  1. 分批次渲染:每次只渲染一小部分数据(比如 50 条)。
  2. 利用 rAF 控制渲染时机,避免阻塞主线程。
  3. 滚动加载优化 (可选):结合 IntersectionObserver 实现懒加载。

代码实现:

javascript 复制代码
function renderLargeData(data, chunkSize = 50) {
  const container = document.getElementById('list');
  let index = 0;

  function renderChunk() {
    // 每次渲染 chunkSize 条数据
    const chunkEnd = Math.min(index + chunkSize, data.length);
    
    for (; index < chunkEnd; index++) {
      const div = document.createElement('div');
      div.textContent = 我是.data[index];
      container.appendChild(div);
    }

    // 如果还有数据,继续渲染
    if (index < data.length) {
      requestAnimationFrame(renderChunk);
    }
  }

  // 开始渲染
  renderChunk();
}

// 测试
const bigData = Array(10000).fill("我是数据项");
renderLargeData(bigData);

优化效果:

页面不卡顿 :每次只渲染少量 DOM,主线程不会被阻塞。

流畅渲染rAF 确保在浏览器空闲时执行,不影响用户交互。

内存可控:不会一次性创建大量 DOM,减少内存压力。


3. 进阶优化:虚拟滚动(Virtual Scroll)

如果你的数据量真的超级大(比如 10W+),即使分批渲染也会占用大量 DOM,这时可以考虑 虚拟滚动(只渲染可视区域内的元素)。

核心思路:

  • 只渲染可见区域的数据,滚动时动态替换内容。
  • 使用 IntersectionObserver 或监听 scroll 事件计算可视范围。
javascript 复制代码
// 简单示例(完整实现会更复杂)
function renderVirtualScroll(data, container, itemHeight = 50) {
  const viewportHeight = container.clientHeight;
  const visibleItemCount = Math.ceil(viewportHeight / itemHeight);
  let startIndex = 0;

  function updateVisibleItems() {
    const endIndex = startIndex + visibleItemCount;
    const visibleData = data.slice(startIndex, endIndex);

    container.innerHTML = '';
    visibleData.forEach(item => {
      const div = document.createElement('div');
      div.style.height = `${itemHeight}px`;
      div.textContent = 我是.item;
      container.appendChild(div);
    });
  }

  container.addEventListener('scroll', () => {
    startIndex = Math.floor(container.scrollTop / itemHeight);
    updateVisibleItems();
  });

  updateVisibleItems();
}

适用场景:

📌 超长列表(如聊天记录、日志数据)

📌 表格数据渲染

📌 无限滚动(Infinite Scroll)


4. 总结:如何选择优化方案?

方案 适用场景 优点 缺点
分批渲染(rAF 数据量中等(1k~10k) 实现简单,兼容性好 DOM 节点仍较多
虚拟滚动 数据量超大(10k+) 极致性能,内存占用低 实现较复杂

我的建议:

  • 如果数据量 < 5000,直接渲染问题不大。
  • 如果数据量 5000~10W ,用 requestAnimationFrame 分批渲染。
  • 如果数据量 > 10W,考虑虚拟滚动或后端分页。

5. 最后的小技巧

  • setTimeout vs rAFrAFsetTimeout 更适合动画/渲染,因为它与浏览器刷新率同步。
  • Web Worker:如果数据处理很耗时(如排序、过滤),可以丢给 Web Worker,避免阻塞 UI。
  • 分页加载:如果可能,尽量让后端分页,减少前端压力。

结语

渲染大数据并不可怕,关键是要避免阻塞主线程requestAnimationFrame 是一个强大的工具,合理使用能让你的页面丝滑流畅!

相关推荐
杨天天.2 分钟前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu12 分钟前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss12 分钟前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师15 分钟前
React面试题
前端·javascript·react.js
木兮xg16 分钟前
react基础篇
前端·react.js·前端框架
ssshooter40 分钟前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘1 小时前
HTML--最简的二级菜单页面
前端·html
yume_sibai2 小时前
HTML HTML基础(4)
前端·html
给月亮点灯|2 小时前
Vue基础知识-Vue集成 Element UI全量引入与按需引入
前端·javascript·vue.js
三思而后行,慎承诺2 小时前
Reactnative实现远程热更新的原理是什么
javascript·react native·react.js