页面加载海量数据
题目:将包含 10w 条记录的数组一次性渲染到页面上,如何处理可以不冻结 UI ?
描述:页面上有一个空的无序列表节点 ul,其 id 为 list-with-big-data,现在需要往列表插入 10w 个 li,每个列表项的文本内容可自行定义,且要求当每个 li 被单击时,通过 alert 显示列表项的文本内容。
1 |
|
分析:我们可能首先会想到:获取 ul 元素,然后新建 li 元素,并设置好 li 的文本内容和监听器绑定,然后在循环里对 ul 进行 append 操作。具体代码如下。
1 | (function() { |
但上述代码带来的界面体验很不友好,卡顿感严重。主要原因是,在每次循环中都会修改 DOM 结构,并且由于数据量大,导致循环执行时间过长,浏览器的渲染帧率过低。
事实上,包含 10w 个 li 的长列表,用户不会立即看到全部,只会看到少部分。因此,对于大部分的 li 的渲染工作,我们可以延时完成,因此可以从减少 DOM 操作次数和缩短循环时间两个方面减少主线程阻塞的时间。
DocumentFragment
The DocumentFragment
interface represents a minimal document object that has no parent. It is used as a lightweight version of Document that stores a segment of a document structure comprised of nodes just like a standard document. The key difference is that because the document fragment isn’t part of the active document tree structure, changes made to the fragment don’t affect the document, cause reflow, or incur any performance impact that can occur when changes are made.
通过使用 DocumentFragment
,可以减少 DOM 操作次数,降低回流对性能的影响。
requestAniminationFrame
The window.requestAnimationFrame()
method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. The method takes a callback as an argument to be invoked before the repaint.
在缩短循环时间方面,我们可以通过 分治 的思想,将 100000 个 li 分批插入到页面中,并且我们通过 requestAniminationFrame
在页面重绘前插入新节点。
事件绑定
如果想监听海量元素,推荐使用 JavaScript 的事件委托,这样可以显著减少 DOM 事件注册的数量。
1 | (function() { |