"虚拟" DOM 操作?

9
我知道多次DOM操作很糟糕,因为它会强制进行多次重新绘制。
例如:
$('body').append('<div />')
         .append('<div />')
         .append('<div />')
         .append('<div />');

相反,更好的做法似乎是:
$('body').append('<div><div></div><div></div><div></div><div></div></div>');

但我对虚拟操作很感兴趣。

即:

$('<div />').append('<div />')
            .append('<div />')
            .append('<div />')
            .append('<div />')
            .appendTo('body');

请问这样做是否还会有不好的影响呢?显然,多次调用函数会带来一些额外开销,但是是否会对性能造成严重的影响呢?


我提出这个问题的原因是:

var divs = [
    {text: 'First',  id: 'div_1', style: 'background-color: #f00;'},
    {text: 'Second', id: 'div_2', style: 'background-color: #0f0;'},
    {text: 'Third',  id: 'div_3', style: 'background-color: #00f;'},
    {text: 'Fourth', id: 'div_4', style: 'background-color: #f00;'},
    {text: 'Fifth',  id: 'div_5', style: 'background-color: #0f0;'},
    {text: 'Sixth',  id: 'div_6', style: 'background-color: #00f;'}
];

var element = $('<div />');

$.each(divs, function(i,o){
    element.append($('<div />', o));
});

$('body').append(element);

假设divs数组来自于描述表单的数据库表(好吧,我在示例中使用div,但它可以很容易地被替换为input)或类似的内容。

或者我们可以使用“推荐”的版本:

var divs = [
    {text: 'First',  id: 'div_1', style: 'background-color: #f00;'},
    {text: 'Second', id: 'div_2', style: 'background-color: #0f0;'},
    {text: 'Third',  id: 'div_3', style: 'background-color: #00f;'},
    {text: 'Fourth', id: 'div_4', style: 'background-color: #f00;'},
    {text: 'Fifth',  id: 'div_5', style: 'background-color: #0f0;'},
    {text: 'Sixth',  id: 'div_6', style: 'background-color: #00f;'}
];

var element = '<div>';

$.each(divs, function(i,o){
    element += '<div ';

    $.each(o, function(k,v){
        if(k != 'text'){
            element += k+'="'+v+'" ';
        }            
    });

    element += '>'+o.text+'</div>';

});

element += '</div>';

$('body').append(element);

@Nathan Hayfield:“似乎还是不好。”---为什么?“为什么不只是使用”---为什么不保持原样呢? - zerkms
@nathan hayfield:“快得多”?你能告诉我对于5个append调用与连接而言的真正区别吗?代码是为人类编写的,只有在不符合性能要求时才进行优化。 - zerkms
2
重绘可能会被推迟到你处理的事件完成之后。 - millimoose
除非您进行需要引擎计算修改节点尺寸的调用,否则它将被重新渲染。 - Ruan Mendes
4个回答

10

首先,虽然了解可能会对性能产生影响很好,但始终应该通过测量来确定是否存在问题。

如果你感觉不到问题,那么编写最易读的代码。

如果你能感觉到问题,那就进行测量、更改和再次测量。

话虽如此,你最后发布的示例涉及尚未写入DOM的元素,因此在appendTo将元素添加到DOM之前不会重新绘制。

我会非常惊讶如果你能捕捉到第二个和第三个示例之间的速度差异,而且如果你能看到它们之间的任何主要差异也会相当惊讶。


但是使用文档片段方法作为最佳实践并没有什么问题(如果可能的话)。 - Ruan Mendes
只要不影响可读性即可。浏览器供应商都在努力使其浏览器超快,所以你只需要关注让你的代码易于理解和修改。 - Fenton
我不确定你编写的应用程序有多大。但是我一直在编写大型企业级应用程序,它们需要高性能,尽管我非常反对过早优化,但我们选择采用了许多已知更快的实践方法...虽然我不喜欢这样做,但我更加讨厌应用在IE上运行缓慢。 - Ruan Mendes
那也是我的想法,如果你看到我问题的编辑,你会发现第三种方法(在我看来)变得最易读,所以我想找出是否有任何需要注意的地方。 - Hailwood
@JuanMendes 如果存在性能问题,当然应该进行修复。我的观点是,更多的工作投入到了潜在的性能问题中。如果有帮助的话,我最近完成了一个实时电话应用程序,需要为数千名呼叫代理提供实时的浏览器更新,所有这些都通过HTML和JavaScript向他们提供服务。 - Fenton
1
@Sohnee 我的观点是,我们没有等到性能下降才采取最佳实践,而是通过简单的方法来加快速度。除非我们在追求性能问题,否则从不需要使用不同的技术来编写肮脏的代码(在这种情况下,任何黑客都是可以接受的)。但我认为我们都同意,保持代码可读性更重要,只是想提醒一下,在这种情况下,它们都很易读,所以你可能会选择使用性能更好的那个。 - Ruan Mendes

1

如果您在添加节点时真的担心性能问题,您需要使用文档片段。这将允许您将元素附加到DOM而不进行重绘。John Resign在这个主题上有一篇优秀的文章。他指出性能提高了200-300%。我在我的一个应用程序中实现了文档片段,并可以确认他的说法。


1
jQueryеңЁжү§иЎҢзұ»дјј$("<div></div>")зҡ„ж“ҚдҪңж—¶пјҢе·Із»ҸеңЁеҶ…йғЁдҪҝз”Ёж–ҮжЎЈзүҮж®өдәҶгҖӮ - Ruan Mendes
@Juan:啊,原来是这样。谢谢你指出来(抱歉,我来自一个普通的背景)。 - Daniel Szabo
需要注意的一点是,这篇文章是4年前写的。在Chrome开发者工具中记录时间线告诉我,他的测试页面只进行了一次布局和绘制。 - millimoose

1

好的旧时标记生成在运行时发生了什么?说真的,发生了什么?

我同意@Sohnee关于可读性重要性的观点,但DOM操作是浏览器可以执行的最昂贵的操作之一。保持标记字符串的选项可以完美地可读,并提供超出微不足道的用户体验改进。

this jsperf中,我们正在创建一个10x100的表格以进行运行时 - 这是一个完全合理的(远非最复杂的场景)数据分页。在运行最新版本Chrome的四核机器上,直接DOM操作脚本需要60ms才能完成,而标记缓存则需要3ms。

这在我的设置中是无法区分的差异,但是对于坐在企业防火墙后面并仍然被迫使用过时版本IE的可怜数学计算人员呢?如果所需的DOM操作更重,包括属性操作和积极强制重新绘制/重新流动呢?

我想说的是,如果您想优化一些JavaScript,那么这不是一个坏的起点。


AJAX发生了;)如果你关心低版本的IE用户,那么也可以提供一个无ajax的版本,但是通常使用javascript标记生成会提供更友好的界面。举个例子,假设你有一个“添加10个属性”的按钮,它可以显示10个额外的输入,当然你可以一开始就生成500个输入并进行显示/隐藏,或者你可以在运行时动态生成这10个输入,如果有意义的话,可以在运行时生成标记,但是确实存在这样的用例。 - Hailwood
@Hailwood: ಠ_ಠ 当然,Ajax 发生了!当我谈到运行时标记生成时,我假设很清楚它是建议在客户端上完成的——即首先将 JSON 数据转换为 HTML 字符串,然后再将其添加到 DOM 中。 - Oleg
那么,在这种情况下,问题就在于可读性,再加上jQuery让我们变懒了,使用jQuery处理构建比自己动手更容易。但是关于运行时标记生成和可读性的问题,这恰好是我在问题中提到的最后一个示例所做的,但是看一下它上面的示例,你会如何使它们同样易读? - Hailwood

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接