我具有以下d3逻辑来渲染单个对象:
svg.selectAll("path")
.data(hugePathDataset)
.enter().append("path")
.attr("class", (d) => d.properties.cls )
.attr("id", (d) => d.properties.name )
.each(... canvas render logic ...)
[出于性能原因,上面的svg
元素设置为display: none
,实际的渲染通过d3
的投影逻辑在画布上进行。仍然需要svg元素,以便以后对画布进行更新(例如分别更改每个路径的颜色)。
我的数据集包含60,000多条路径,以上代码需要大约30秒才能运行。在Chrome的探查器中对其进行测试,我注意到90%的时间都花在了回流焊上。这对我来说毫无意义,因为画布不会重排,并且带有display: none
的SVG不应重排DOM。当我继续研究它时,我意识到重排不是通过将元素附加到不可见的SVG来触发的,而是通过在这些元素上设置class
和id
属性来触发的。果然,如果我删除第4行和第5行,回流速度的降低将完全消失。设置其他属性(例如data-something
)不会导致速度降低/重排。
问题是,如上所述,我之后无法再分别操纵这些路径。我的问题是:
class
或id
添加到父级设置为display: none
的元素会触发重排?阅读D3文档,我意识到selection.append("path")
等效于selection.append(() => document.createElement("path"))
。由于document.createElement
尚未将元素附加到DOM,因此可以安全地在其上设置属性而不进行重排。我以不同的方式重写了以上逻辑,问题消失了:
svg.selectAll("path")
.data(hugePathDataset)
.enter().append((d) => {
let element = document.createElement("path");
element.id = d.properties.name;
element.className = d.properties.cls;
return element;
})
.each(... canvas render logic ...)
我仍然不明白为什么不可见元素上的类/ id更改会导致重排,但是我不再为此受阻。