题目:10w 条记录的数组,一次性渲染到页面上,如何处理可以不冻结UI?
页面上有个空的无序列表节点 ul
,其 id
为 list-with-big-data
,现需要往列表插入 10w 个 li
,每个列表项的文本内容可自行定义,且要求当每个 li
被单击时,通过 alert
显示列表项内的文本内容。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>页面加载海量数据</title> </head> <body> <ul id="list-with-big-data">100000 数据</ul> <script> // 此处添加你的代码逻辑 </script> </body> </html>
分析:我们可以从 减少 DOM 操作次数 和 缩短循环时间 两个方面减少主线程阻塞的时间。
可以通过 DocumentFragment
的使用,减少 DOM 操作次数,降低回流对性能的影响。
在缩短循环时间方面,我们可以通过 分治 的思想,将 100000 个 li
分批插入到页面中,并且我们通过 requestAniminationFrame
在页面重绘前插入新节点。
(function() { const ulContainer = document.getElementById("list-with-big-data"); // 防御性编程 if (!ulContainer) { return; } const total = 100000; // 插入数据的总数 const batchSize = 4; // 每次批量插入的节点个数,个数越多,界面越卡顿 const batchCount = total / batchSize; // 批处理的次数 let batchDone = 0; // 已完成的批处理个数 function appendItems() { // 使用 DocumentFragment 减少 DOM 操作次数,对已有元素不进行回流 const fragment = document.createDocumentFragment(); for (let i = 0; i < batchSize; i++) { const liItem = document.createElement("li"); liItem.innerText = batchDone * batchSize + i + 1; fragment.appendChild(liItem); } // 每次批处理只修改 1 次 DOM ulContainer.appendChild(fragment); batchDone++; doAppendBatch(); } function doAppendBatch() { if (batchDone < batchCount) { // 在重绘之前,分批插入新节点 window.requestAnimationFrame(appendItems); } } // kickoff doAppendBatch(); // 使用 事件委托 ,利用 JavaScript 的事件机制,实现对海量元素的监听,有效减少事件注册的数量 ulContainer.addEventListener("click", function(e) { const target = e.target; if (target.tagName === "LI") { alert(target.innerText); } }); })();