📢最近准备面试啦了,所以整理了一些前端高频大厂面试题,分享给大家,如有问题欢迎留言指正,面试专栏我会长期更新,欢迎大家点赞🤞、收藏📌,关注➕,感谢! 大家在面试的时候,面试官问你一次如何渲染10万条数据,这个是时候,你应该怎么回答呢,接下来,我来跟你细讲一下。
直接渲染
让我们看看有什么样的结果吧
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="container"></ul>
<script>
const total = 100000
let ul = document.getElementById('container')
for (let i = 0; i < total; i++) {
let li = document.createElement('li')
li.innerHTML = `这是${i + 1}个新的列表项`
ul.appendChild(li)
}
</script>
</body>
</html>
展示效果
缺点 :
- 这样会加载起来比较慢。
- 页面渲染很久
- 十万次的回流
requestAnimationFrame + fragment(时间分片)
requestAnimationFrame 是浏览器提供的一个用于优化动画性能的 API。它的作用是请求浏览器在下次重绘之前执行指定的回调函数,以保证动画的流畅性和性能。
在 Web 开发中,通常使用 JavaScript 来创建动画效果。过去常用的方法是使用 setInterval 或 setTimeout 来反复执行更新动画状态的代码,但这些方法存在一些问题,比如:
- 时间不准确:
setInterval和setTimeout的时间间隔不一定准确,可能会导致动画卡顿或者过度绘制。 - 不受垂直同步信号控制:这些方法不能与浏览器的垂直同步信号(即垂直同步刷新率)同步,可能导致动画撕裂或不稳定。
requestAnimationFrame 的出现解决了这些问题。它的工作原理是,浏览器会在每次重绘(通常是每秒 60 次)之前调用注册的回调函数,以确保动画在适当的时机进行更新,同时与垂直同步信号保持同步,提高了动画的流畅性和性能。
使用 requestAnimationFrame 的基本流程如下:
- 定义一个更新动画状态的回调函数。
- 使用
requestAnimationFrame注册这个回调函数,浏览器会在下次重绘之前调用它。 - 在回调函数中更新动画状态。
- 在回调函数中再次调用
requestAnimationFrame,以请求下一帧动画。
这样就能够实现一个基于 requestAnimationFrame 的流畅、高性能的动画效果。
fragment在 JavaScript 中,“fragment” 并没有一个特定的内置概念或者语言特性,但在某些情况下,“fragment” 可以指代一种虚拟的 DOM 元素容器或者文档片段。
文档片段(DocumentFragment) : 在原生 JavaScript 中,文档片段(DocumentFragment)是一种特殊的 DOM 节点,它可以作为一个轻量级的容器,用于临时存放 DOM 元素。文档片段本身不会直接插入到文档中,而是可以包含其他 DOM 元素,当文档片段插入到文档中时,它的子元素会被移动到目标位置,这样可以减少 DOM 操作的性能消耗。
<script>
const ul = document.getElementById('container')
const total = 10000
const couter = 20
function loop(total) {
total = total - couter
if (total <= 0) return
console.log(1111)
window.requestAnimationFrame(() => {
let pageCount = Math.min(total, couter)
let fragment = document.createDocumentFragment()//// 创建一个虚拟的文档片段
for (let i = 0; i <= pageCount; i++) {
let li = document.createElement('li')
li.innerText = `这是${i + 1}个新的列表项`
fragment.appendChild(li)
}
ul.appendChild(fragment)
loop(total)
})
}
loop(total)
</script>
效果展示
fragment是虚拟文档碎片,我们一次for循环产生 20 个li的过程中可以全部把真实dom挂载到fragment上,然后再把fragment挂载到真实dom上,这样原来需要回流十万次,现在只需要回流100000 / 20次,减少了回流的次数
虚拟列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Virtual Scroll</title>
<style>
#container {
width: 300px;
height: 600px;
overflow-y: scroll;
border: 1px solid #ccc;
position: relative;
}
.item {
height: 20px;
line-height: 20px;
border-bottom: 1px solid #eee;
position: absolute;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
// 生成10万条假数据
const data = [];
for (let i = 0; i < 10000; i++) {
data.push(`Item ${i}`);
}
const container = document.getElementById('container');
const itemHeight = 20; // 每个条目的高度
const visibleItemCount = Math.ceil(container.clientHeight / itemHeight); // 可见的条目数量
function renderItems(startIndex, endIndex) {
for (let i = startIndex; i <= endIndex; i++) {
if (i >= data.length) {
break; // 防止索引超出数据范围
}
const item = document.createElement('div');
item.className = 'item';
item.textContent = data[i];
item.style.top = `${i * itemHeight}px`; // 设置每个条目的位置
container.appendChild(item);
}
}
function handleScroll() {
const scrollTop = container.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = startIndex + visibleItemCount;
container.innerHTML = ''; // 清空容器中的内容
renderItems(startIndex, endIndex);
}
handleScroll(); // 初始化滚动事件处理
container.addEventListener('scroll', handleScroll);
</script>
</body>
</html>
当用户滚动页面时,JavaScript 部分起到了关键作用:
- 生成数据:通过循环生成了包含 10 万条假数据的数组
data。这些数据将被用于填充列表中的条目。 - 渲染条目:
renderItems(startIndex, endIndex)函数负责渲染列表中的条目。传入的参数是起始索引和结束索引,表示当前可见区域内的数据范围。该函数会创建<div>元素来表示每个条目,并将它们附加到容器中。 - 处理滚动事件:
handleScroll()函数被绑定到容器的滚动事件上。每当用户滚动容器时,该函数被触发。它计算出当前滚动位置,根据滚动位置计算出可见的条目范围,并调用renderItems()函数重新渲染可见的条目。 - 初始化和滚动时的处理:在页面加载完成后,会首先调用
renderItems()函数渲染首屏可见的条目。此后,当用户滚动容器时,会触发滚动事件,进而调用handleScroll()函数来动态更新可见的条目。
这样的实现方式带来了性能的提升,因为只有可见区域内的条目会被实际渲染到页面上,而不是一次性渲染整个数据集。这对于大型数据集来说尤为重要,因为它能够减少页面渲染的工作量,从而提高页面加载速度和用户体验。
如觉得本文对你有帮助的话,欢迎点赞❤❤❤,写作不易,持续输出的背后是无数个日夜的积累,您的点赞是持续写作的动力,感谢支持!有什么写错的,需要增加的可以在评论区留言 谢谢大家支持