【问题标题】:Performance with infinite scroll or a lot of dom elements?无限滚动或大量 dom 元素的性能?
【发布时间】:2012-09-18 18:01:24
【问题描述】:

我有一个关于 dom elmenets 和性能的问题。

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

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

  1. 将不可见项目的显示设置为无以避免重排
  2. 从 dom 中移除不可见的项目,然后根据需要重新添加它们。

他们还有其他方法可以改进包含大量 dom 元素的页面吗?

【问题讨论】:

  • 将显示设置为无会触发回流,如果您想要避免回流,这与您想要的完全相反。不做任何事情不会触发回流。将可见性设置为隐藏也不会触发重排。但是,做任何事情都不是一件容易的事。
  • 您还应该知道,您的窗口管理器已经从屏幕上移除了不可见的像素以加快 UI 交互。用 javascript 自己做可能会减慢速度而不是加快速度。
  • @slebetman // 当我将显示设置为无时,它会触发重排。确实如此。但是..当您滚动时,它也会触发重排,并且浏览器必须计算页面上所有元素的布局。我认为对页面上的某些项目设置不显示将有助于减少布局计算时间。
  • 不,它没有。您可以通过调试打开并通过探查器运行它来自己检查它。滚动触发重绘,所有程序中的所有滚动甚至 Word 或记事本都会触发重绘。即使移动鼠标指针也会触发重绘。但是重绘很便宜——它们自 1980 年代后期以来已经被优化,并且由操作系统/窗口管理器本身而不是程序实现。滚动不会触发重排。
  • @slebetman // 啊..我从paulirish.com/2011/dom-html5-css3-performance 视频中得到了滚动回流的印象。该视频列出了 window.scrollBy 导致回流。我想知道为什么当用户的滚动没有时它会这样做。

标签: javascript performance dom


【解决方案1】:

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

我注意到 Facebook 的时间线似乎使用了内容集群,一旦集群消失,就会使用 display: none !important 隐藏,因此所有之前渲染的 DOM 元素都保存在 DOM 中,只是那些看不见的东西用display: none !important 隐藏。 另外,隐藏簇的总高度设置为隐藏簇的父div

以下是我制作的一些截图:

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

感谢您的关注!

【讨论】:

  • 有一个叫react-window的库,和上面的解释很接近
【解决方案2】:

我们不得不在FoldingText 上处理类似的问题。随着文档变得越来越大,创建了更多的线元素和相关的跨度元素。浏览器引擎似乎卡住了,因此需要找到更好的解决方案。

以下是我们所做的,可能对您的目的有用,也可能没用:

将整个页面可视化为一个长文档,将浏览器视口可视化为长文档特定部分的镜头。你真的只需要展示镜头内的部分。

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

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

更多资源可用于查找更多基于跨浏览器的视口:

Get the browser viewport dimensions with JavaScript

Cross-browser method for detecting the scrollTop of the browser window

其次,你需要一个数据结构来知道哪些元素在那个区域是可见的

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

var boundingRect = element.getBoundingClientRect()

我说的是纯 javascript,但如果您使用 jQuery $.offset$.position 和列出的方法 here 会很有帮助。

再次重申,使用数据结构仅作为缓存很重要,但如果您愿意,您可以随时进行(尽管我已经说过这些操作很昂贵)。另外,请注意更改 css 样式和调用这些方法。这些函数会强制重绘,因此您会看到性能问题。

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

  • 现在,您有了存储在数据结构中的所有元素的高度,查询位于可见视口之前的所有元素。

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

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

对位于可见视口之后的元素重复此操作。

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

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

tl;博士

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

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

希望这会有所帮助。

【讨论】:

  • // 有道理!感谢您的洞察力!
  • 你能举一个需要复杂数据结构的例子吗?
  • 顺便说一句,我发现这是处理大量 DOM 元素并具有合理滚动体验的唯一方法。此外,在(jquery)滚动处理程序中获取 scrollTop 不会导致重绘。事实上它永远不会这样做,它只会清除重绘和重绘队列,以便可以返回最新的 scrollTop。我的猜测是在处理滚动事件时它已经是最新的了。
  • @JorenVanSeveren 这取决于您对复杂数据结构的需求。正如我所说,我们使用了平衡二叉树,因为我们已经有了一个。我认为您不需要它来管理 dom 的高度。
  • @Moon:此答案中概述的相同技术已以插件的形式实现。 ClusterizeJS。完全按预期工作,如果您需要,它还支持项目奇偶校验。它真的很好用! (仅供参考:我不是它的开发者)
【解决方案3】:

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

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

Twitter 似乎与 Facebook 相同。

Google 地图是另一回事 - 不在视野范围内的地图图块会从 DOM 中移除(尽管不是立即)。

【讨论】:

  • // 感谢您花时间调查您的发现。我很快就会这样做。
  • 看看 Pinterest 是如何做到的会很有趣。当您向下滚动和向上滚动时,他们似乎已经找到了动态加载元素的方法。他们似乎总是在任何给定时间在 Feed 页面上加载固定数量的 Pin 图。
猜你喜欢
  • 2016-05-05
  • 2012-02-29
  • 2015-02-11
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多