使用无限滚动或许多dom元素进行演奏?


76

我对大量的dom elmenets和性能有疑问。

假设我在一个页面上有6000个dom元素,并且随着用户与该页面进行交互(用户滚动创建一个新的dom元素)(例如twitter),可以增加元素的数量。

为了提高页面的性能,我只能想到两件事。

  1. 将不显示的项目设置为“无”,以避免回流
  2. 从dom中删除不可见的项目,然后根据需要重新添加。

他们是否还有其他方法可以改善包含许多dom元素的页面?


3
将显示设置为无会触发重排,这与您想要避免重排完全相反。不执行任何操作不会触发重排。将可见性设置为隐藏也不会触发重排。但是,做任何事情都不容易。
slebetman 2012年

3
您还应该注意,窗口管理器已经从屏幕上删除了不可见的像素,从而加快了UI交互。自己使用javascript进行操作可能会减慢速度,而不是加快速度。
slebetman 2012年

@slebetman //当我将display设置为none时,它将触发重排。确实如此。但是..当您滚动时,它还会触发重排,浏览器必须计算页面上所有元素的布局。我认为在页面上的某些项目上不显示任何内容将有助于减少布局计算时间。
月亮

1
不,不是。您可以通过以下方法自己检查出来:调试时编译firefox或chrome,然后通过分析器运行它。滚动触发重新绘制,所有程序(包括Word或记事本)中的所有滚动都触发重新绘制。即使移动鼠标指针也会触发重新绘制。但是重绘很便宜-自1980年代末以来就已经对其进行了优化,并且由OS /窗口管理器本身而不是程序来实现。滚动不会触发重排。
slebetman 2012年

1
@slebetman //啊..我从paulirish.com/2011/dom-html5-css3-performance视频获得了滚动回流的印象。该视频列出了window.scrollBy导致重排。我不知道为什么当用户的滚动条不滚动时会如此。
月亮

Answers:


26

我自己没有经验,但是这里有一些很棒的提示:http : //engineering.linkedin.com/linkedin-ipad-5-techniques-smooth-infinite-scrolling-html5

我看了一下Facebook,他们似乎并没有做什么特别是在Firefox上。向下滚动时,页面顶部的DOM元素不变。在Facebook不允许您进一步滚动之前,Firefox的内存使用量已攀升至约500兆。

Twitter似乎与Facebook相同。

Google地图是另一回事-看不见的地图图块已从DOM中删除(尽管不是立即删除)。


1
//感谢您抽出宝贵时间调查您的发现。我很快也会做。
月亮

2
看看Pinterest是如何做到的,将真的很有趣。他们似乎已经解决了这一问题,因为在您上下滚动时可以动态地加载元素。在任何给定时间,它们似乎总是在供稿页面上加载了一定数量的引脚。
亚历克斯2015年

103

我们必须在FoldingText上处理类似的问题。随着文档的增大,创建了更多的线元素和关联的跨度元素。浏览器引擎似乎令人窒息,因此需要找到更好的解决方案。

这是我们所做的事情,可能对您有用或可能不有用:

将整个页面可视化为长文档,将浏览器视口可视为长文档特定部分的镜头。您实际上只需要显示镜头内的零件即可。

因此,第一部分是计算可见视口。(这取决于元素的放置方式,绝对/固定/默认)

var top = document.scrollTop;
var width = window.innerWidth;
var height = window.innerHeight;

一些更多的资源来查找更多基于跨浏览器的视口:

使用JavaScript获取浏览器视口尺寸

跨浏览器方法,用于检测浏览器窗口的scrollTop

其次,您需要一个数据结构来了解该区域中哪些元素可见

我们已经有一个平衡的二进制搜索树用于文本编辑,因此我们也对其进行了扩展以管理行高,因此对于我们来说,这部分相对容易。我认为您不需要复杂的数据结构来管理元素高度。一个简单的数组或对象可能会很好。只要确保您可以轻松查询高度和尺寸即可。现在,您将如何获取所有元素的高度数据。非常简单(但是对于大量元素而言,计算量很大!)

var boundingRect = element.getBoundingClientRect()

我说的是纯JavaScript,但是如果您使用的是jQuery $.offset,那么此处$.position列出的方法将非常有帮助。

同样,使用数据结构仅作为缓存很重要,但是,如果需要,您可以即时进行(尽管正如我所说的,这些操作非常昂贵)。另外,请注意更改css样式并调用这些方法。这些功能会强制重绘,因此您会看到性能问题。

最后,只需将单个元素替换为屏幕外的元素,例如<div>具有已计算高度的元素

  • 现在,您已经拥有存储在Data结构中的所有元素的高度,可以查询可见视口之前的所有元素。

  • 创建一个<div>css高度设置为元素高度之和的像素(以像素为单位)

  • 用一个类名标记它,以便您知道它是一个填充div
  • 从此div覆盖的dom中删除所有元素
  • 插入这个新创建的div代替

对可见视口之后的元素重复上述步骤。

查找滚动和调整大小事件。在每个滚动上,您将需要返回到数据结构,删除填充器div,创建以前从屏幕上删除的元素,并相应地添加新的填充器div。

:)这是一个漫长而复杂的方法,但是对于大型文档,它大大提高了我们的性能。

tl; dr

我不确定我是否解释正确,但是此方法的要点是:

  • 了解元素的垂直尺寸
  • 了解滚动视图端口
  • 用一个div表示所有屏幕外元素(高度等于其涵盖的所有元素高度的总和)
  • 在任何给定时间,您总共需要两个div,一个用于可见视口上方的元素,一个用于下方视口的元素。
  • 通过侦听滚动和调整大小事件来跟踪视口。相应地重新创建div和可见元素

希望这可以帮助。


// 那讲得通!感谢您的见解!
月亮

您能否举一个需要复杂数据结构的例子?
Joren Van Severen

3
顺便说一句,我发现这是在保持合理滚动体验的情况下处理许多DOM元素的唯一方法。另外,在(jquery)滚动处理程序中获取scrollTop不会导致重绘。实际上,它从来没有这样做,它只清除重新绘制和重新绘制的队列,以便可以返回最新的scrollTop。我的猜测是,滚动事件处理的那一刻它已经是最新的了。
Joren Van Severen

1
@JorenVanSeveren取决于您的需求re:复杂的数据结构。就像我说的,我们使用了平衡的二叉树,因为我们已经有了一棵。我认为您只需要管理dom的高度就不需要它。
Mutahhir

8
@Moon:此答案中概述的相同技术已以插件的形式实现。ClusterizeJS。完全按预期工作,如果需要,它还支持项目奇偶校验。它真的很棒!(仅供参考:我不是开发商)
罗伯特·科里特尼克

8

现在是2019年。这个问题确实很老,但是我认为它仍然是相关且有趣的,并且可能从今天起有所改变,因为我们现在都倾向于使用React JS。

我注意到,Facebook的时间轴似乎使用了内容簇,这些簇display: none !important一旦消失就被隐藏了,因此DOM的所有先前呈现的元素都保留在DOM中,只是那些看不见的部分被隐藏了。display: none !important。同样,隐藏群集的整体高度设置为隐藏群集的父级div

这是我制作的一些屏幕截图:

在此处输入图片说明

在此处输入图片说明

在此处输入图片说明

截至2019年,您如何看待这种方法?另外,对于那些使用React的人,如何在React中实现它?收到您对这个棘手主题的意见和想法,将是很棒的。

感谢您的关注!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.