html页面加载海量数据的实现

更新日期: 2019-08-10 阅读: 2.7k 标签: 数据

题目

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>

分析

可能在看到这个问题的第一眼,我们可能会想到这样的解决办法:获取 ul 元素,然后新建 li 元素,并设置好 li 的文本内容和监听器绑定,然后在循环里对 ul 进行 append 操作,即可能想到的是以下代码实现。

(function() {
  const ulContainer = document.getElementById("list-with-big-data");

  // 防御性编程
  if (!ulContainer) {
    return;
  }

  for (let i = 0; i < 100000; i++) {
    const liItem = document.createElement("li");

    liItem.innerText = i + 1;
    // EventListener 回调函数的 this 默认指向当前节点,若使用箭头函数,得谨慎
    liItem.addEventListener("click", function() {
      alert(this.innerText);
    });
    ulContainer.appendChild(liItem);
  }
})();
复制代码

实践上述代码,我们发现界面体验很不友好,卡顿感严重。出现卡顿感的主要原因是,在每次循环中,都会修改 dom 结构,并且由于数据量大,导致循环执行时间过长,浏览器的渲染帧率过低。

事实上,包含 100000 个 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.

在 MDN 的介绍中,我们知道可以通过 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 事件注册 的数量。


解决方案

经过上面的讨论,我们有了如下的解决方案。

(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);
    }
  });
})();

作者:Jiahonzheng
链接:https://juejin.im/post/5ae17a386fb9a07abc299cdd

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/4605

相关推荐

js判断数字是奇数还是偶数的2种方法实现

奇数和偶数的判断是数学运算中经常碰到的问题,这篇文章主要讲解通过JavaScript来实现奇偶数的判断。2种判断方法:求余% 、&1

进制转换_二进制、八进制和十六进制数之间的转换

在计算机语言中常用的进制有二进制、八进制、十进制和十六进制,十进制是最主要的表达形式。对于进制,有两个基本的概念:基数和运算规则。

mock.js模拟数据

开发时,后端还没完成数据输出,前端只好写静态模拟数据。数据太长了,将数据写在js文件里,完成后挨个改url。某些逻辑复杂的代码,加入或去除模拟数据时得小心翼翼。想要尽可能还原真实的数据,要么编写更多代码,要么手动修改模拟数据

几个数据持久化框架Hibernate、JPA、Mybatis、JOOQ和JDBC Template的比较

因为项目需要选择数据持久化框架,看了一下主要几个流行的和不流行的框架,对于复杂业务系统,最终的结论是,JOOQ是总体上最好的,可惜不是完全免费,最终选择JDBC Template。

nodejs 使用 xlsx 实现导入导出

将数据导出成excel方法,下面介绍两种方式,一种是将数组数据导出成excel,一种是将json数据导出成excel,都非常简单

web scraper 抓取网页数据的几个常见问题

如果你想抓取数据,又懒得写代码了,可以试试 web scraper 抓取数据。如果你在使用 web scraper 抓取数据,很有可能碰到如下问题中的一个或者多个,而这些问题可能直接将你计划打乱,甚至让你放弃 web scraper 。

双向数据绑定与单向数据绑定的各自优势和关系

在react中是单向数据绑定,而在vue和augular中的特色是双向数据绑定。为什么会选择两种不同的机制呢?我猜测是两种不同的机制有不同的适应场景,查了一些资料后,总结一下。

原生JS数据绑定的实现

双向数据绑定是非常重要的特性 —— 将JS模型与HTML视图对应,能减少模板编译时间同时提高用户体验。我们将学习在不使用框架的情况下,使用原生JS实现双向绑定 —— 一种为Object.observe

JavaScript判断数据类型的多种方法【 js判断一个变量的类型】

js判断数据类型的多种方法,主要包括:typeof、instanceof、 constructor、 prototype.toString.call()等,下面就逐一介绍它们的异同。

Nginx返回大长度的JSON数据被截断

1 添加Nginx参数,增加缓存字符串大小;2 遇到权限问题,原因是大文件会先缓存到/proxy-temp文件夹下面,然后再返回;修改文件夹的权限为Nginx用户

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!