一次渲染十万条数据:前端技术优化(上)

news2024/11/14 17:49:04

今天看了一篇文章,写的是一次性渲染十万条数据的方法,本文内容是对这篇文章的学习总结,以及知识点补充。

在现代Web应用中,前端经常需要处理大量的数据展示,例如用户评论、商品列表等。直接渲染大量数据会导致浏览器性能问题,如卡顿和延迟。本文将探讨几种优化策略,帮助开发者提高网页性能,优化用户体验。

方法一:通过document直接渲染十万条数据

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>一次性渲染十万条数据</title>
</head>
<body>
  <ul id="app"></ul>
</body>
<script>
  const app = document.querySelector('#app')
  let now = Date.now()
  // 方法一:一次性渲染十万条数据
  for (let i = 0; i < 100000; i++) {
   const li = document.createElement('li')
   li.innerText = `我是第${i + 1}条数据`
   app.appendChild(li)
  }


  console.log('js运行耗时', Date.now() - now)

  setTimeout(() => {
    console.log('dom渲染耗时', Date.now() - now)
  })
  
</script>
</html>

结果

请添加图片描述

问题

当页面需要一次性渲染大量数据时,直接将所有数据渲染到DOM中会迅速消耗浏览器资源,造成性能瓶颈。这种方法虽然简单,但会导致浏览器响应缓慢,用户体验差。

补充知识1: JS事件循环之宏任务和微任务

在 JavaScript 中,事件循环是处理并发的机制,它允许执行非阻塞的操作。事件循环的核心概念包括宏任务(macro tasks)和微任务(micro tasks)。它们的执行顺序和机制十分重要,理解它们有助于更好地编写异步代码。

宏任务与微任务
  1. 宏任务 (Macro Task):

    • 宏任务是较大粒度的任务,它通常由以下几部分组成:
      • 整个脚本(执行的上下文)
      • setTimeout
      • setInterval
      • I/O 操作(如网络请求)
    • 宏任务的执行是按照创建顺序依次执行的。
  2. 微任务 (Micro Task):

    • 微任务是相对较小粒度的任务,通常用于处理短小的异步操作,主要由以下几部分组成:
      • Promise.then().catch()
      • MutationObserver
    • 微任务在当前宏任务执行完毕后立即执行,且在浏览器进行下一次重绘之前完成所有微任务。这意味着微任务的优先级高于宏任务。
执行顺序
  1. 首先,事件循环从宏任务队列中取出一个宏任务并执行。
  2. 执行完宏任务后,查看微任务队列,执行所有微任务,直到微任务队列为空。
  3. 一旦微任务队列为空,浏览器会进行渲染(重绘),然后再从宏任务队列中取出下一个宏任务。
  4. 重复这个过程,直到所有任务都完成。
示例

下面是一个简单的示例,帮助理解宏任务和微任务的执行顺序:

console.log('Start');

setTimeout(() => {
  console.log('Timeout 1');
}, 0);

new Promise((resolve, reject) => {
  console.log('Promise 1');
  resolve('Promise 1 resolved');
}).then((res) => {
  console.log(res);
});

process.nextTick(() => {
  console.log('Next Tick');
});

setTimeout(() => {
  console.log('Timeout 2');
}, 0);

new Promise((resolve, reject) => {
  console.log('Promise 2');
  resolve('Promise 2 resolved');
}).then((res) => {
  console.log(res);
});

console.log('End');
执行输出

执行上面的代码将会输出以下内容:

Start
Promise 1
Promise 2
End
Promise 1 resolved
Promise 2 resolved
Next Tick
Timeout 1
Timeout 2
  • 在 JavaScript 的事件循环中,宏任务的执行优先于微任务,但在宏任务完成后,微任务会立即执行,确保在下一个宏任务开始之前执行所有微任务。
  • 理解这一流程能够帮助开发者更好地预测和管理异步代码的执行顺序及其结果。

补充知识2: 执行渲染操作,更新界面为什么发生在执行 setTimeout之前?

在 JavaScript 中,事件循环和异步操作的处理方式是理解 setTimeout 和界面更新之间关系的关键。

界面更新的时机

在 JavaScript 中,界面更新通常在以下几个时刻发生:

  • 当主线程空闲时(即没有其他任务在执行时),浏览器会进行界面更新。
  • 在一个宏任务(如 setTimeout)执行之后,浏览器会检查微任务队列并执行所有的微任务,然后再进行界面更新。
为什么界面更新发生在执行 setTimeout 之前
  1. 执行顺序:当你调用 setTimeout 时,传入的回调函数不会立即执行,而是被放入宏任务队列中。主线程会继续执行当前的任务。
  2. 界面更新:在当前任务结束后,浏览器会检查是否有需要更新的界面。此时,如果有 DOM 的改变(例如通过某个函数修改了 DOM),浏览器会更新界面。
  3. 执行 setTimeout 的回调:在主线程空闲并完成微任务后,浏览器会从宏任务队列中取出 setTimeout 的回调并执行。
示例
console.log("Start");
setTimeout(() => {
    console.log("Inside setTimeout");
}, 0);
console.log("End");

输出顺序将是:

Start
End
Inside setTimeout

在 JavaScript 中,界面更新发生在当前任务完成后和 setTimeout 回调执行之前。理解这一点对于优化性能和确保用户界面流畅性非常重要。

补充知识3: 回流与重绘

页面的回流与重绘是指浏览器在渲染网页时的两种重要过程。

  1. 回流(Reflow):当页面的结构或内容发生变化时(比如增加、删除或修改元素的大小、位置等),浏览器需要重新计算元素的几何属性,以确定它们的位置和大小。这一过程称为回流。回流会影响整个文档的布局,因此较为耗性能。
  2. 重绘(Repaint):当元素的外观发生变化(比如背景色、字体颜色等)但并不影响布局时,浏览器只需重新绘制该元素,而无需重新计算其几何属性。这一过程称为重绘。重绘通常比回流消耗的性能要少。
    总结来说,回流是布局计算,重绘是外观更新。在优化网页性能时,应尽量减少这两个过程的发生。

方法二:分批渲染

为了解决直接渲染带来的性能问题,我们可以采用分批渲染的方法。通过将数据分成小块,逐一渲染,可以减轻浏览器的即时负担。

使用 setTimeout 进行分批渲染

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>一次性渲染十万条数据</title>
</head>
<body>
  <ul id="app"></ul>
</body>
<script>
  const app = document.querySelector('#app')
  let now = Date.now()
    // 方法二:分批渲染十万条数据
  const total = 100000
  const loadOnce = 20
  const page = total / loadOnce
  const index = 0
  function renderData(curTotal, curIndex) {
    let pageCount=Math.min(loadOnce,curTotal)

    setTimeout(() => {
      for (let i = 0; i < pageCount; i++) {
        const li = document.createElement('li')
        li.innerText = `我是第${i}条数据`
        app.appendChild(li)
      }
      renderData(curTotal-pageCount, curIndex + pageCount)
    })
  }
    renderData(total, index)

  </script>
</html>

结果

请添加图片描述

问题

当用户往下翻的时候有可能那一瞬间看不到东西

方法三、使用requestAnimationFrame替代setTimeout

requestAnimationFrame 是一种更高效的分批渲染方法,它允许在浏览器的绘制周期中执行动画和渲染,从而提高性能。

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>一次性渲染十万条数据</title>
</head>
<body>
  <ul id="app"></ul>
</body>
<script>
  const app = document.querySelector('#app')
  let now = Date.now()
    // 方法三:使用requestAnimationFrame渲染十万条数据
  const total = 100000
  const loadOnce = 20
  const page = total / loadOnce
  const index = 0
  function renderData(curTotal, curIndex) {
    let pageCount=Math.min(loadOnce,curTotal)

    requestAnimationFrame(() => {
      for (let i = 0; i < pageCount; i++) {
        const li = document.createElement('li')
        li.innerText = `我是第${i}条数据`
        app.appendChild(li)
      }
      renderData(curTotal-pageCount, curIndex + pageCount)
    })
  }
    renderData(total, index)

  </script>
</html>

补充知识4: 什么是requestAnimationFrame?

requestAnimationFrame 是一个用于优化网页动画效果的 JavaScript 方法。它指示浏览器在下次重绘之前调用指定的回调函数,从而实现基于帧的动画效果。使用 requestAnimationFrame 的一个主要优点是它能够根据浏览器的绘制频率来调整动画的更新速率,从而使动画更加流畅和高效。

使用场景:
  1. 平滑动画:如果你在进行平移动画、旋转、缩放等效果时,使用 requestAnimationFrame 可以确保动画的每一帧都在浏览器的绘制周期内更新,这有助于避免由于使用 setTimeoutsetInterval 而导致的帧率不稳定。
  2. 减少 CPU 消耗requestAnimationFrame 还具有智能调节的功能,特别是在用户切换标签页或者浏览器窗口不在视野内时,它会自动停止调用回调函数,从而节省资源。

基本用法:

function animate() {
    // 更新动画状态
    // ...
    // 请求下一帧
    requestAnimationFrame(animate);
}
// 开始动画
requestAnimationFrame(animate);

在上面的代码中,animate 函数会执行动画的逻辑,并通过 requestAnimationFrame(animate) 请求下一帧的更新。这样,animate 函数会在浏览器准备好下一个绘制周期时被调用。

优势:

  • 帧率同步requestAnimationFrame 会使动画的帧率与浏览器的刷新率(通常是60帧每秒)同步,从而提高流畅性。

  • 性能优化:当页面不在视野中时,requestAnimationFrame 会暂停动画的执行,从而减少 CPU 和 GPU 的使用。

  • 简洁易用:API 简单直观,更容易使用来控制动画的生命周期。

总之,使用 requestAnimationFrame 是在现代网页应用中实现高性能动画的推荐方式。

方法四:利用 DocumentFragment

DocumentFragment 是一个轻量级的文档对象,可以用于在内存中组装一组节点,然后一次性添加到DOM中,减少DOM操作次数。

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>一次性渲染十万条数据</title>
</head>
<body>
  <ul id="app"></ul>
</body>
<script>
  const app = document.querySelector('#app')
  let now = Date.now()
    // 方法四:使用文档碎片渲染十万条数据
  const total = 100000
  const loadOnce = 20
  const page = total / loadOnce
  const index = 0
  function renderData(curTotal, curIndex) {
    let documentFragment = document.createDocumentFragment()
    let pageCount=Math.min(loadOnce,curTotal)

    requestAnimationFrame(() => {
      for (let i = 0; i < pageCount; i++) {
        const li = document.createElement('li')
        li.innerText = `我是第${i}条数据`
        documentFragment.appendChild(li)
      }
      app.appendChild(documentFragment)
      renderData(curTotal-pageCount, curIndex + pageCount)
    })
  }

  renderData(total, index)
  </script>
</html>

总结

在处理大量数据渲染时,选择合适的方法至关重要。直接渲染虽然简单,但性能较差。分批渲染、requestAnimationFrameDocumentFragment 提供了更优的性能解决方案。开发者应根据具体情况选择最合适的方法,以确保应用的流畅性和用户体验。

补充知识5:什么是DocumentFragment?

DocumentFragment 是一个轻量级的文档对象,表示一个可以包含多个节点的虚拟容器。它不是文档中的实际部分,而是用来在内存中组装一组节点,然后一次性地将它们添加到文档中。这种做法可以提高性能,因为它减少了对 DOM 的多次操作,降低了重绘和重排的次数。

作用和优势:
  1. 性能优化:直接多次操作 DOM 会导致浏览器频繁地重绘和重排,从而降低性能。使用 DocumentFragment 可以将多个 DOM 操作合并为一个,从而显著提高性能。

  2. 内存管理DocumentFragment 只存在于内存中,直到你将它的内容添加到实际的 DOM 中。这使得在构建大型 DOM 结构时更节省内存。

  3. 灵活性:你可以使用 DocumentFragment 来临时组装和修改多个节点,然后一次性插入到 DOM 中,保持 DOM 的一致性。

基本用法:

下面是一个简单的例子,展示如何使用 DocumentFragment 来添加多个节点到 DOM 中:


// 创建一个 DocumentFragment

const fragment = document.createDocumentFragment();

// 创建几个新的元素

const li1 = document.createElement('li');

li1.textContent = 'Item 1';

const li2 = document.createElement('li');

li2.textContent = 'Item 2';

const li3 = document.createElement('li');

li3.textContent = 'Item 3';

// 将元素添加到 DocumentFragment 中

fragment.appendChild(li1);

fragment.appendChild(li2);

fragment.appendChild(li3);


// 将 DocumentFragment 追加到现有的 DOM 中

document.getElementById('myList').appendChild(fragment);

在上面的示例中,使用 DocumentFragment 创建并组合了多个 li 元素,最后一次性将它们添加到一个列表中。这样可以避免在将每个 li 添加到 DOM 时引起的多次重排和重绘。

DocumentFragment 是一个非常有用的工具,适用于需要频繁与 DOM 交互时,帮助保持性能和优化操作。在需要插入或修改多个节点时,使用 DocumentFragment 是个不错的选择。

参考文章:
https://juejin.cn/post/7407763018471948325

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2144451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PMP出成绩非常慢?PDU如何获取?

1、PMP项目管理认证考试后6-8周才公布成绩 2、非一次性发布&#xff0c;从第一个人成绩公布到最后一个公布结束持续一周左右时间 3、PMP每年会考4次&#xff08;3月、5月、8月、11月&#xff09; 4、组织方为美国 5、报名费3900&#xff0c;补考费2500 PMP考试成绩查询方式…

新任项目经理,如何快速接手项目

新任项目经理踏上管理岗位&#xff0c;面临着前所未有的挑战与机遇。如何快速适应角色&#xff0c;有效管理团队&#xff0c;确保项目顺利推进&#xff0c;是每一位新任项目经理必须面对的。 一、深入了解项目与团队 1、项目定位与规划 新任项目经理应对项目进行全面的调研与…

7款实用电脑办公软件和网站,工作效率飞跃提升

4款软件3个网站&#xff0c;电脑里一定要有这7款实用办公工具&#xff0c;提升效率杠杠滴&#xff0c;用过以后就离不开了&#xff01; 1、Free Download Manager 一个免费的下载神器&#xff0c;不仅界面简洁&#xff0c;还没广告。能设置下载计划&#xff0c;多线程下载&am…

让3000年都无人能识的伪二重直线段一下子浮出水面推翻百年集论

黄小宁 公元前1100年中国人商高同周公的一段对话谈到了勾股定理说明人类认识几何学的直线段起码已有3000多年。 直线可由无穷多直线段连接而成。直线段可均匀收缩变短。长为2的长条形弹簧W&#xff08;可二等分&#xff09;被压缩变短成长为1的弹簧b&#xff0c;b不是W的一半。…

yolov8区域入侵检测警报系统-pyside6可视化界面

yolov8区域入侵检测警报系统&#xff0c;是微智启软件工作室基于yolov8目标追踪和pyside6开发&#xff0c;在window的pycharm或者vscode里运行&#xff0c;可以应用于多个领域&#xff0c;检测统计物体个数以及入侵语音警报。 功能介绍 可以应用于 江河流域危险区域禁止游泳警…

C#图像爬虫实战:从Walmart网站下载图片

无论是电子商务网站、社交媒体平台还是新闻门户&#xff0c;图像都扮演着至关重要的角色。对于开发者来说&#xff0c;能够自动化地从这些网站下载图片是一项非常有用的技能。本文将介绍如何使用C#语言和CsQuery库来创建一个图像爬虫&#xff0c;专门用于从Walmart网站下载图片…

空洞骑士 Hollow Knight 攻略

文章目录 Part.I IntroductionPart.II 攻略Chap.I 地图Chap.II 护符Chap.III BOSSChap.IV 魔法 Reference Part.I Introduction 《空洞骑士》是独立团队 Team Cherry 开发的一款 2D 类银河恶魔城动作冒险游戏。本文记录笔者玩此游戏所积累的一些经验。 「在《空洞骑士》中打造…

【C++】多态的认识和理解

个人主页 文章目录 ⭐一、多态的概念&#x1f384;二、多态的定义及实现1.多态的构成2.实现多态的条件3.虚函数的概念4.虚函数的重写和覆盖5.析构函数的重写6.协变7.override和 final关键字8.重载、重写/覆盖、隐藏这三者的区别 &#x1f3e0;三、纯虚函数和抽象类的关系&#…

智能边缘网关深入剖析-天拓四方

随着物联网、大数据和云计算等技术的飞速发展&#xff0c;智能边缘网关作为一种新兴的技术与应用逐渐走入人们的视野。本文将从全新视角对智能边缘网关进行深入剖析&#xff0c;阐述其定义、功能、重要性及其在工业领域的应用。 一、定义 智能边缘网关是集成了数据采集、处理…

低代码开发平台系统架构概述

概述 织信低代码开发平台&#xff08;产品全称&#xff1a;织信Informat&#xff09;是一款集成了应用设计、运行与管理的综合性平台。它提供了丰富的功能模块&#xff0c;帮助用户快速构建、部署和维护应用程序。织信低代码平台通过集成丰富的功能模块&#xff0c;为用户提供…

Redhat 7,8系(复刻系列) 一键部署Oracle21c-xe rpm

Oracle21c-xe前言 无论您是开发人员、DBA、数据科学家、教育工作者,还是仅仅对数据库感兴趣,Oracle Database Express Edition (XE) 都是理想的入门方式。它是全球企业可依赖的强大的 Oracle Database,提供简单的下载、易于使用和功能齐全的体验。您可以在任何环境中使用该…

物业的帮手,中央空调分户计费系统

随着现代科技的飞速发展&#xff0c;建筑管理和能源消耗的智能化已成为不可逆转的趋势。传统按面积收费的中央空调计费模式存在诸多弊端&#xff0c;例如能源浪费、费用不透明、物业纠纷频发等问题。为了解决这些问题&#xff0c;一种能够测量和记录中央空调所消耗的能源&#…

【已解决】IDEA鼠标光标与黑块切换问题,亲测有效

前言 前两天我妹妹说她室友的idea光标变成黑块状了&#xff0c;解决不了跑来问我&#xff0c;这是刚入门开发者经常遇到的问题&#xff0c;这篇文章介绍一下这两种方式&#xff0c;方便刚入门的小伙伴儿们更清楚地了解idea&#xff0c;使用idea。 希望这篇文章能够帮助到遇到…

硬件工程师笔试面试——变压器

目录 9、变压器 9.1 基础 变压器原理图 变压器实物图 9.1.1 概念 9.1.2 变压器组成结构 9.1.3 变压器原理 9.1.4 变压器的类型 9.1.5 应用领域 9.2 相关问题 9.2.1 变压器的工作原理是什么? 9.2.2 如何选择合适的变压器类型? 9.2.3 变压器在实际应用中,如何进行…

百川智能在 AI Agent 领域的思考与探索 —— 2024 稀土开发者大会总结

引言 在 2024 年稀土开发者大会上&#xff0c;百川智能的马宝昌先生分享了百川在 AI Agent 领域的最新探索与思考&#xff0c;展示了百川如何通过大模型技术的创新推动 Agent 应用的发展。这次演讲涵盖了从 AI 基础技术、强化学习、多模态模型&#xff0c;到具体的 Agent 应用…

从数据仓库到数据中台再到数据飞轮:社交媒体的数据技术进化史

前言 大家好&#xff0c;我是在大数据方面具有一定理解的博主。今天我想分享下从数据仓库到数据中台再到数据飞轮:社交媒体的数据技术进化史&#xff0c;也是这篇文章主题。我亲眼目睹了社交媒体的快速发展&#xff0c;以及随之而来的海量数据的生成与积累。如何有效地管理和利…

安泰功率放大器在超声行业中的应用有哪些

超声技术是一种在医疗、工业、科学等领域广泛应用的非侵入性、高分辨率的检测和成像技术。功率放大器在超声领域中扮演着至关重要的角色&#xff0c;它们不仅仅是信号的增强器&#xff0c;更是推动超声技术发展的关键组件。下面西安安泰电子官网将深入介绍功率放大器在超声行业…

【Linux】基础IO认识(2)

基础IO认识&#xff08;2&#xff09; 1、补充系统调用1、1、read调用1、2、stat 2、重定向2、1、文件描述符的分配规则2、2、实现重定向(dup2) 3、缓冲区的理解3、1、缓冲区典型实例3、2、缓冲区代码形式展示 4、深化和实践利用4、1、在shell中加入重定向4、2、简单实现库的封…

Axios基本语法和前后端交互

Axios是一个js框架&#xff0c;用于发送ajax请求。 一、导入 // node中&#xff0c;使用npm安装 npm install axios // HTML中&#xff0c;使用cdn安装 <script src"https://unpkg.com/axios/dist/axios.min.js"></script> 二、基本使用 // 使用axios…

MTK平台--蓝牙驱动数据加载的过程

前言: 先看这张图可以知道架构 LinuxKernel层: bluez协议栈、uart驱动, h4协议, hci,l2cap, sco, rfcomm Library层: libbluedroid.so 等 Framework层: 实现了Headset /Handsfree 和 A2DP/AVRCP profile,但其实现方式不同Handset/Handfree是直接 在bluez的RFCOMM So…