当前位置: 首页 > news >正文

页面实现渲染大量 DOM 元素

页面实现渲染大量 DOM 元素

一、使用 setTimeout 进行分批渲染

1.1、实现方案

function process() {for (let i = 0; i < batchSize; i++) {if (processed >= total) return;const div = document.createElement('div');...fatherDom.appendChild(div);processed++;};if (processed < total) {setTimeout(process, 0);}
}

1.2、原理解析

  • setTimeout(fn, 0) 并不是“立即执行”,而是将 fn 推到宏任务队列中,等待当前执行栈清空后再执行。
  • 每次执行只插入 batchSize 个元素,然后将“下一批任务”再次放到事件循环中,以避免主线程被长时间阻塞。
  • 浏览器有机会在任务之间处理用户输入、重绘等操作,提升响应性。

1.3、优点

  • 相对简单。

  • 在旧浏览器中也能良好工作。

1.4、缺点

  • 不精确:setTimeout 受浏览器调度器控制,延迟时间不确定。

  • 宏任务调度会占用较多主线程时间,导致页面流畅性和用户交互能力下降。

  • 页面滚动/动画可能变卡。

二、使用 requestAnimationFrame 进行分批渲染

2.1、实现方案

function processBatch() {requestAnimationFrame(() => {for (let i = 0; i < batchSize; i++) {if (processed >= total) return;const div = document.createElement('div');...fatherDom.appendChild(div);processed++;}if (processed < total) {processBatch();}});
}

2.2、原理解析

requestAnimationFrame(fn) 是浏览器专门为动画或逐帧任务设计的接口:

  • 回调 fn 会在 下一帧绘制之前 执行。
  • 浏览器通常以 60 FPS 为目标(即每 16.67ms 调用一次回调)。
  • 它是一个 微任务与宏任务之间的“帧任务”调度机制,更适合用于逐帧渲染或分批插入 DOM。

2.3、优点

  • 更平滑:浏览器知道你要进行渲染操作,会自动协调布局和绘制的时机。
  • 不会阻塞 UI 渲染,可保持高帧率和良好响应性。
  • 性能更高效,尤其适合动画、渲染大量内容等任务。

2.4、缺点

  • 每帧执行次数有限,不适合一次性添加太多元素
  • 如果 batchSize 设置过大,还是会卡顿

三、虚拟列表 / 虚拟滚动(Virtual Scrolling)

3.1、🧠 原理概述

虚拟列表是一种按需渲染技术。你只渲染当前屏幕中可见的元素,其他元素并不会真实存在于 DOM 中。这样即使你有 100 万个数据项,页面中也只存在几十甚至更少的 DOM 节点

3.2、✅ 核心优点

  • 实际 DOM 数量小(几十个),性能极高;

  • 滚动平滑,不会出现卡顿;

  • 适合超大数据量列表(10万~1000万);

  • 可和分页、异步加载等策略结合

3.3 ✅ 推荐实现方式

✅ 方式一:使用成熟库(强烈推荐)

👉 React-Virtualized

  • React 专用,功能丰富、性能强悍。
  • 支持表格、列表、瀑布流等。

👉 Vue-Virtual-Scroller

  • Vue 专用。
  • 支持动态高度、平滑滚动等。

👉 Clusterize.js(适合原生 JS)

  • 纯原生 JS 实现,适合不使用框架的场景;
  • 易集成,超轻量(< 5KB)

✅ 方式二:手动实现一个简单虚拟列表(原生 JS-示意)

<style>.viewport {height: 500px;overflow-y: scroll;border: 1px solid #ccc;position: relative;}.spacer {height: 2000000px; /* 假设一百万个,每个20px */}.item {position: absolute;height: 20px;line-height: 20px;width: 100%;padding-left: 1rem;box-sizing: border-box;}
</style><div class="viewport" id="viewport"><div class="spacer" id="spacer"></div>
</div><script>const viewport = document.getElementById("viewport");const spacer = document.getElementById("spacer");const itemHeight = 20;const totalItems = 1000000;const visibleCount = Math.ceil(viewport.clientHeight / itemHeight) + 10;const pool = [];for (let i = 0; i < visibleCount; i++) {const div = document.createElement("div");div.className = "item";viewport.appendChild(div);pool.push(div);}function render() {const scrollTop = viewport.scrollTop;const start = Math.floor(scrollTop / itemHeight);for (let i = 0; i < pool.length; i++) {const index = start + i;if (index >= totalItems) {pool[i].style.display = "none";} else {pool[i].style.display = "block";pool[i].style.top = (index * itemHeight) + "px";pool[i].textContent = `这是第 ${index + 1}`;}}}viewport.addEventListener("scroll", render);render(); // 初始渲染
</script>

四、🧠 总结:几种方案对比

方案渲染性能用户体验DOM 数量优点缺点
setTimeout 分批渲染中等容易卡顿实现简单容易丢帧,主线程阻塞
requestAnimationFrame较好比较平滑更流畅DOM 太多仍然卡
虚拟滚动非常好极为流畅极低性能极佳,真实可用实现稍复杂,需要计算映射关系
Web Worker + Canvas非常好非 DOM 场景0可视化强,不卡顿只能用于图形渲染,非 DOM 操作

五、❓适用建议

  • ✅ 要处理百万数据项,强烈推荐虚拟滚动;
  • ❌ 不要直接往页面插入 100 万个 DOM 元素;
  • 🧪 想测试浏览器性能,可以用分批方式;
  • 🎨 如果要进行图形展示,如地理数据、节点图,考虑使用 Canvas/WebGL(如 D3.js, Pixi.js, Cesium)

相关文章:

  • 【GESP真题解析】第 12 集 GESP 二级 2024 年 3 月编程题 1:乘法问题
  • Spring Boot中使用AMQP协议与RabbitMQ
  • stream数据流
  • 0304考试通过-逻辑回归实战-机器学习-人工智能
  • 40-智慧医疗服务平台(在线接/问诊/机器学习)
  • 机器学习课程设计报告 —— 基于二分类的岩石与金属识别模型
  • 如何保证 Kafka 数据实时同步到 Elasticsearch?
  • 【MySQL】第7节|Mysql锁机制与优化实践以及MVCC底层原理剖析
  • 预分配矩阵内存提升文件数据读取速度
  • Kotlin中let、run、with、apply及also的差别
  • 【Python/Pygame】事件监测
  • [C语言初阶]扫雷小游戏
  • Java 函数式接口(Functional Interface)
  • 符合Python风格的对象(使用 __slots__ 类属性节省空间)
  • DeepSeek 赋能数字农业:从智慧种植到产业升级的全链条革新
  • Windows 中动态库.dll 的 .lib 文件有什么作用?
  • SOC-ESP32S3部分:10-GPIO中断按键中断实现
  • 什么是模板字符串?比普通字符串的好处
  • mongodb语法$vlookup性能分析
  • YOLO11解决方案之使用 Streamlit 应用程序进行实时推理
  • 做网站用主机/搜索引擎营销sem包括
  • 如何查询网站接入商/企业网络推广网站
  • 网站二维码收费怎么做/联合早报 即时消息
  • 青岛高端模板建站/小程序开发公司十大排名
  • 巴西网站建设/2023第二波疫情已经到来
  • 永康网站建设服务/万网域名注册查询