补充已经发布的答案。
我的测试结果要点:
- 将元素设置为
display: none;
可以减少RAM使用量
- 不显示的元素不受布局变化的影响,因此在这方面没有(或非常少的)性能成本
- Firefox 在处理大量元素时效果更好(约x50)
还要尽量减少布局变化。
这是我的测试设置:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
margin: 0;
}
.testEl {
width: 100%;
height: 10px;
}
</style>
</head>
<body>
<main id="main"></main>
</body>
<script>
// firefox Max
// const elementCount = 12200000;
// chrome Max
const elementCount = 231000;
const main = document.getElementById("main");
const onlyShowFirst1000 = true;
let _content = ""
for (let i = 0; i < elementCount; i++) {
_content += `<div class="testEl" style="background-color: hsl(${(Math.random() * 360)|0}, 100%, 50%); display: ${!onlyShowFirst1000 || i < 1000 ? "block" : "none"}"></div>`;
}
main.innerHTML = _content;
const addOneEnd = () => {
const newEl = document.createElement("div");
newEl.classList.add("testEl");
newEl.style.backgroundColor = `hsl(${(Math.random() * 360)|0}, 100%, 50%)`
requestAnimationFrame(() => {
main.appendChild(newEl);
})
};
const addOneBeginning = () => {
const newEl = document.createElement("div");
newEl.classList.add("testEl");
newEl.style.backgroundColor = `hsl(${(Math.random() * 360)|0}, 100%, 50%)`
requestAnimationFrame(() => {
main.insertBefore(newEl, main.firstChild);
})
};
const loop = (front = true) => {
front ? addOneBeginning() : addOneEnd();
setTimeout(() => loop(front), 100);
};
</script>
</html>
我创建了许多元素,并使用
onlyShowFirst1000
标志选项仅显示前1000个元素。当显示所有元素时,Firefox 允许最多 ~12200000 个元素(使用我10GB的RAM),而Chrome 允许最多 ~231000 个元素。
内存使用情况(在231000个元素处):
+
| false | true | reduction % |
+
| Chrome | 415,764k | 243,096k | 42% |
+
| Firefox | 169.9MB | 105.7MB | 38% |
+
将元素的显示属性从无到有或从有到无会导致该区域被重绘,但是您的元素区域通常会相对较小,因此性能成本也会很小。但是,根据您的布局,显示更改可能还会导致布局移位,这可能非常昂贵,因为它会导致页面的大部分重新绘制。
在未来(例如Chrome 85),您还可以使用
content-visibility
属性告诉浏览器哪些元素不必呈现。
另外,您可以使用开发工具设置浏览器显示重绘情况,在Chrome中打开渲染选项卡并勾选“闪烁绘制”。
display: none
并不会保留元素的高度。display: none
将元素完全从流中移除。visibility: hidden
将使它们不显示,但在布局中保留其位置和尺寸。如果Facebook正在这样做,他们一定有一个不是display:none
的元素来维护高度。但是他们很可能在这些元素内部使用display:none
的包装器,这样当重新流动发生时,这些元素中可能存在的(可能复杂的)布局逻辑就不必再次发生了。 - T.J. Crowderreact-virtualized
是一个不错的解决方案:https://bvaughn.github.io/react-virtualized/#/components/Collection - Lucas D