Javascript 无限滚动或大量 dom 元素的性能?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12613113/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Performance with infinite scroll or a lot of dom elements?
提问by Moon
I have a question on a big # of dom elmenets and performance.
我有一个关于大量 dom elmenets 和性能的问题。
Let's say I have 6000 dom elements on a page and the number of the elements can be increased as a user interact with the page (user scrolls to create a new dom element) like twitter.
假设我在一个页面上有 6000 个 dom 元素,并且元素的数量可以随着用户与页面交互(用户滚动以创建新的 dom 元素)而增加,例如 twitter。
To improve the performance of the page, I can think of only two things.
为了提高页面的性能,我只能想到两件事。
- set display to none to invisible items to avoid reflow
- remove invisible items from the dom then re-add them as needed.
- 将 display 设置为 none 以显示不可见的项目以避免回流
- 从 dom 中删除不可见的项目,然后根据需要重新添加它们。
Are they any other ways of improving a page with a lot of dom elements?
它们是否还有其他方法可以改进具有大量 dom 元素的页面?
采纳答案by cbp
No experience myself with this, but there are some great tips here: http://engineering.linkedin.com/linkedin-ipad-5-techniques-smooth-infinite-scrolling-html5
我自己没有这方面的经验,但这里有一些很棒的提示:http: //engineering.linkedin.com/linkedin-ipad-5-techniques-smooth-infinite-scrolling-html5
I had a look at Facebook and they don't seem to do anything in particular on Firefox. As you scroll down, the DOM elements at the top of the page don't change. Firefox's memory usage climbs to about 500 meg before Facebook doesn't allow you to scroll further.
我查看了 Facebook,他们似乎没有在 Firefox 上做任何特别的事情。当您向下滚动时,页面顶部的 DOM 元素不会改变。在 Facebook 不允许您进一步滚动之前,Firefox 的内存使用量攀升至大约 500 兆。
Twitter appears to be the same as Facebook.
Twitter 似乎与 Facebook 相同。
Google Maps is a different story - map tiles out of view are removed from the DOM (although not immediately).
谷歌地图是一个不同的故事 - 从 DOM 中删除了看不见的地图图块(尽管不是立即)。
回答by Mutahhir
We had to deal with a similar problem on FoldingText. As the document grew larger, more line elements and associated span elements were created. The browser engine just seemed to choke, and so a better solution needed to be found.
我们不得不在FoldingText上处理类似的问题。随着文档变大,更多的线元素和相关的跨度元素被创建。浏览器引擎似乎卡住了,因此需要找到更好的解决方案。
Here's what we did, may or may not be useful for your purposes:
这是我们所做的,可能对您的目的有用,也可能没用:
Visualize the entire page as a long document, and the browser viewport as the lens for a specific part of the long document. You really only have to show the part within the lens.
将整个页面可视化为一个长文档,并将浏览器视口视为长文档特定部分的镜头。你真的只需要展示镜头内的部分。
So the first part is to calculate the visible view port. (This depends on how your elements are placed, absolute / fixed / default)
所以第一部分是计算可见视口。(这取决于您的元素如何放置,绝对/固定/默认)
var top = document.scrollTop;
var width = window.innerWidth;
var height = window.innerHeight;
Some more resources to find a more cross-browser based viewport:
找到更多基于跨浏览器的视口的更多资源:
Get the browser viewport dimensions with JavaScript
Cross-browser method for detecting the scrollTop of the browser window
Second, you need a data structure to know which elements are visible in that area
其次,您需要一个数据结构来知道该区域中哪些元素可见
We already had a balanced binary search tree in place for text editing, so we extended it to manage line heights too, so this part for us was relatively easy. I don't think you'll need a complex data structure for managing your element heights; a simple array or object might do fine. Just make sure you can query heights and dimensions easily on it. Now, how would you get the height data for all your elements. A very simple (but computationally expensive for large amounts of elements!)
我们已经有一个用于文本编辑的平衡二叉搜索树,所以我们也扩展了它来管理行高,所以这部分对我们来说相对容易。我认为您不需要复杂的数据结构来管理元素高度;一个简单的数组或对象可能会很好。只要确保您可以轻松查询高度和尺寸。现在,您将如何获取所有元素的高度数据。一个非常简单的(但是对于大量元素来说计算成本很高!)
var boundingRect = element.getBoundingClientRect()
I'm talking in terms of pure javascript, but if you're using jQuery $.offset
, $.position
, and methods listed herewould be quite helpful.
我说的是在纯JavaScript的方面,但如果你使用jQuery $.offset
,$.position
和列出的方法在这里是非常有帮助的。
Again, using a data structure is important only as a cache, but if you want, you could do it on the fly (though as I've stated these operations are expensive). Also, beware of changing css styles and calling these methods. These functions force redraw, so you'll see a performance issue.
同样,使用数据结构仅作为缓存很重要,但如果您愿意,您可以即时进行(尽管正如我所说,这些操作很昂贵)。另外,请注意更改 css 样式和调用这些方法。这些函数强制重绘,因此您会看到性能问题。
Lastly, just replace the elements offscreen with a single, say <div>
element with calculated height
最后,只需用单个元素替换屏幕外的元素,例如<div>
具有计算高度的元素
Now, you have heights for all the elements stored in your Data structure, query all the elements that lie beforethe visible viewport.
Create a
<div>
with css height set (in pixels) to the sum of the element heights- Mark it with a class name so that you know its a filler div
- Remove all the elements from the dom that this div covers
- insert this newly created div instead
现在,您拥有存储在数据结构中的所有元素的高度,查询位于可见视口之前的所有元素。
创建一个
<div>
css 高度设置(以像素为单位)为元素高度的总和- 用类名标记它,以便您知道它是一个填充 div
- 从这个 div 覆盖的 dom 中删除所有元素
- 插入这个新创建的 div
Repeat for elements that lie afterthe visible viewport.
对位于可见视口之后的元素重复此操作。
Look for scroll and resize events. On each scroll, you will need to go back to your data structure, remove the filler divs, create elements that were previously removed from screen, and accordingly add new filler divs.
查找滚动和调整大小事件。在每次滚动时,您都需要返回到您的数据结构,删除填充 div,创建以前从屏幕上删除的元素,并相应地添加新的填充 div。
:) It's a long, complex method, but for large documents it increased our performance by a large margin.
:) 这是一个漫长而复杂的方法,但对于大型文档,它大大提高了我们的性能。
tl;dr
tl;博士
I'm not sure I explained it properly, but the gist of this method is:
我不确定我是否正确解释了它,但这种方法的要点是:
- Know the vertical dimensions of your elements
- Know the scrolled view port
- Represent all off-screen elements with a single div (height equal to the sum of all element heights it covers for)
- You will need two divs in total at any given time, one for elements above the visible viewport, one for elements below.
- Keep track of the view port by listening for scroll and resize events. Recreate the divs and visible elements accordingly
- 了解元素的垂直尺寸
- 了解滚动视口
- 用单个 div 表示所有离屏元素(高度等于它所覆盖的所有元素高度的总和)
- 在任何给定时间,您总共需要两个 div,一个用于可见视口上方的元素,一个用于下方的元素。
- 通过侦听滚动和调整大小事件来跟踪视口。相应地重新创建 div 和可见元素
Hope this helps.
希望这可以帮助。
回答by tonix
It's 2019. The question is really old, but I think it is still relevant and interesting and maybe something changed as of today, as we all now also tend to use React JS.
现在是 2019 年。这个问题真的很老了,但我认为它仍然是相关和有趣的,也许到今天有所改变,因为我们现在也倾向于使用 React JS。
I noticed that Facebook's timeline seems to use clusters of content which is hidden with display: none !important
as soon as the cluster goes out of view, so all the previously rendered elements of the DOM are kept in the DOM, it's just that those out of view are hidden with display: none !important
.
Also, the overall height of the hidden cluster is set to the parent div
of the hidden cluster.
我注意到 Facebook 的时间线似乎使用了内容集群,display: none !important
一旦集群消失,这些内容就会被隐藏起来,所以 DOM 之前呈现的所有元素都保留在 DOM 中,只是那些不在视野中的元素被隐藏起来display: none !important
. 此外,隐藏簇的整体高度设置为隐藏簇的父级div
。
Here are some screenshots I've made:
以下是我制作的一些屏幕截图:
As of 2019, what do you think about this approach? Also, for those who use React, how could it be implemented in React? It would be great to receive your opinions and thoughts regarding this tricky topic.
截至 2019 年,您如何看待这种方法?另外,对于那些使用 React 的人,它如何在 React 中实现?收到您对这个棘手话题的意见和想法会很棒。
Thank you for the attention!
感谢您的关注!