多少钱网站设计,投资公司怎么赚钱,flash网站后台,设计一套网页要多少钱今天看了一篇文章#xff0c;写的是一次性渲染十万条数据的方法#xff0c;本文内容是对这篇文章的学习总结#xff0c;以及知识点补充。
在现代Web应用中#xff0c;前端经常需要处理大量的数据展示#xff0c;例如用户评论、商品列表等。直接渲染大量数据会导致浏览器性…今天看了一篇文章写的是一次性渲染十万条数据的方法本文内容是对这篇文章的学习总结以及知识点补充。
在现代Web应用中前端经常需要处理大量的数据展示例如用户评论、商品列表等。直接渲染大量数据会导致浏览器性能问题如卡顿和延迟。本文将探讨几种优化策略帮助开发者提高网页性能优化用户体验。
方法一通过document直接渲染十万条数据
示例代码
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title一次性渲染十万条数据/title
/head
bodyul idapp/ul
/body
scriptconst 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。它们的执行顺序和机制十分重要理解它们有助于更好地编写异步代码。
宏任务与微任务 宏任务 (Macro Task): 宏任务是较大粒度的任务它通常由以下几部分组成 整个脚本执行的上下文setTimeoutsetIntervalI/O 操作如网络请求 宏任务的执行是按照创建顺序依次执行的。 微任务 (Micro Task): 微任务是相对较小粒度的任务通常用于处理短小的异步操作主要由以下几部分组成 Promise 的 .then() 和 .catch()MutationObserver 微任务在当前宏任务执行完毕后立即执行且在浏览器进行下一次重绘之前完成所有微任务。这意味着微任务的优先级高于宏任务。
执行顺序
首先事件循环从宏任务队列中取出一个宏任务并执行。执行完宏任务后查看微任务队列执行所有微任务直到微任务队列为空。一旦微任务队列为空浏览器会进行渲染重绘然后再从宏任务队列中取出下一个宏任务。重复这个过程直到所有任务都完成。
示例
下面是一个简单的示例帮助理解宏任务和微任务的执行顺序
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 之前
执行顺序当你调用 setTimeout 时传入的回调函数不会立即执行而是被放入宏任务队列中。主线程会继续执行当前的任务。界面更新在当前任务结束后浏览器会检查是否有需要更新的界面。此时如果有 DOM 的改变例如通过某个函数修改了 DOM浏览器会更新界面。执行 setTimeout 的回调在主线程空闲并完成微任务后浏览器会从宏任务队列中取出 setTimeout 的回调并执行。
示例
console.log(Start);
setTimeout(() {console.log(Inside setTimeout);
}, 0);
console.log(End);输出顺序将是
Start
End
Inside setTimeout在 JavaScript 中界面更新发生在当前任务完成后和 setTimeout 回调执行之前。理解这一点对于优化性能和确保用户界面流畅性非常重要。
补充知识3: 回流与重绘
页面的回流与重绘是指浏览器在渲染网页时的两种重要过程。
回流Reflow当页面的结构或内容发生变化时比如增加、删除或修改元素的大小、位置等浏览器需要重新计算元素的几何属性以确定它们的位置和大小。这一过程称为回流。回流会影响整个文档的布局因此较为耗性能。重绘Repaint当元素的外观发生变化比如背景色、字体颜色等但并不影响布局时浏览器只需重新绘制该元素而无需重新计算其几何属性。这一过程称为重绘。重绘通常比回流消耗的性能要少。 总结来说回流是布局计算重绘是外观更新。在优化网页性能时应尽量减少这两个过程的发生。
方法二分批渲染
为了解决直接渲染带来的性能问题我们可以采用分批渲染的方法。通过将数据分成小块逐一渲染可以减轻浏览器的即时负担。
使用 setTimeout 进行分批渲染
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title一次性渲染十万条数据/title
/head
bodyul idapp/ul
/body
scriptconst app document.querySelector(#app)let now Date.now()// 方法二分批渲染十万条数据const total 100000const loadOnce 20const page total / loadOnceconst index 0function renderData(curTotal, curIndex) {let pageCountMath.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 langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title一次性渲染十万条数据/title
/head
bodyul idapp/ul
/body
scriptconst app document.querySelector(#app)let now Date.now()// 方法三使用requestAnimationFrame渲染十万条数据const total 100000const loadOnce 20const page total / loadOnceconst index 0function renderData(curTotal, curIndex) {let pageCountMath.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 的一个主要优点是它能够根据浏览器的绘制频率来调整动画的更新速率从而使动画更加流畅和高效。
使用场景
平滑动画如果你在进行平移动画、旋转、缩放等效果时使用 requestAnimationFrame 可以确保动画的每一帧都在浏览器的绘制周期内更新这有助于避免由于使用 setTimeout 或 setInterval 而导致的帧率不稳定。减少 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 langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title一次性渲染十万条数据/title
/head
bodyul idapp/ul
/body
scriptconst app document.querySelector(#app)let now Date.now()// 方法四使用文档碎片渲染十万条数据const total 100000const loadOnce 20const page total / loadOnceconst index 0function renderData(curTotal, curIndex) {let documentFragment document.createDocumentFragment()let pageCountMath.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总结
在处理大量数据渲染时选择合适的方法至关重要。直接渲染虽然简单但性能较差。分批渲染、requestAnimationFrame 和 DocumentFragment 提供了更优的性能解决方案。开发者应根据具体情况选择最合适的方法以确保应用的流畅性和用户体验。
补充知识5:什么是DocumentFragment
DocumentFragment 是一个轻量级的文档对象表示一个可以包含多个节点的虚拟容器。它不是文档中的实际部分而是用来在内存中组装一组节点然后一次性地将它们添加到文档中。这种做法可以提高性能因为它减少了对 DOM 的多次操作降低了重绘和重排的次数。
作用和优势 性能优化直接多次操作 DOM 会导致浏览器频繁地重绘和重排从而降低性能。使用 DocumentFragment 可以将多个 DOM 操作合并为一个从而显著提高性能。 内存管理DocumentFragment 只存在于内存中直到你将它的内容添加到实际的 DOM 中。这使得在构建大型 DOM 结构时更节省内存。 灵活性你可以使用 DocumentFragment 来临时组装和修改多个节点然后一次性插入到 DOM 中保持 DOM 的一致性。
基本用法
下面是一个简单的例子展示如何使用 DocumentFragment 来添加多个节点到 DOM 中 // 创建一个 DocumentFragmentconst 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