当没有任何改变时,触碰DOM是否会触发回流和重绘?

6

我正在开发一个小型JavaScript模板引擎,当模型发生更改时,我有两种处理DOM更新的可能方法:

  1. Check if the DOM update is really needed before doing it. This has the benefit of not risking unnecessary updates, but I am wasting space on keeping track of old values.

    if (oldValue !== newValue) {
        element.textContent = newValue;
    }
    
  2. Just do it. This is obviously simpler, but I am afraid that I will be triggering repaints and reflows for no reason.

    element.textContent = newValue;
    
请注意,我还通过调用 setAttributeaddClassremoveClass,以及设置 style[prop] = value 来操作DOM。
因此,我的问题是:如果您在不实际更改任何内容的情况下触摸DOM,现代浏览器是否足够聪明,以便注意到没有实际更改,因此不运行回流或重绘?

2
现在的浏览器非常智能。如果没有显示方面的变化,就不会发生重新排版。即使有变化,重绘也只会应用于受影响的屏幕部分。 - Bekim Bacaj
“我在浪费空间。” 是的,如今空间是稀缺资源,我的台式机只有32GB的内存。 - user663031
2
@torazaburo 在大型单页应用程序中保留所有绑定值的副本在移动设备上成本高昂,如果它们经常更改,则会产生大量需要收集的垃圾,从而破坏帧速率。这不是一个很大的影响,但如果没有做这件事情的零点,仍然可能有一些可获得的东西。 - Anders
1个回答

6
使用MutationObserver api,您可以检测DOM更改。
以下是一个示例,您可以使用它来查看浏览器是否根据您的需求触发了Dom Changed事件。
这里既有jquery的text('...'),也有不使用jquery的el.textContent

$(document).ready(function() {
  $('#btn1').click(function() {
    console.log('text changed - jquery');
    $('#a1').text('text 1');
  });
  $('#btn2').click(function() {
    console.log('text changed - textContent');
    $('#a1')[0].textContent  = $('#a1')[0].textContent 
  });
  $('#btn3').click(function() {
    console.log('class changed');
    $('#a1').attr('class', 'cls' + Math.floor(Math.random() * 10));
  });
});


var target = $('#a1')[0];

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  var changed = false;
  mutations.forEach(function(mutation) {
    // You can check the actual changes here
  });
  console.log('Dom Changed');
});

// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };

// pass in the target node, as well as the observer options
observer.observe(target, config);
.cls1 {
  border: 1px solid red;
}
.cls2 {
  border: 1px solid pink;
}
.cls3 {
  border: 1px solid cyan;
}
.cls4 {
  border: 1px solid darkgreen;
}
.cls5 {
  border: 1px solid orange;
}
.cls6 {
  border: 1px solid darkred;
}
.cls7 {
  border: 1px solid black;
}
.cls8 {
  border: 1px solid yellow;
}
.cls9 {
  border: 1px solid blue;
}
.cls10 {
  border: 1px solid green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="a1" class="cls1">text 1</div>
<button id="btn1">Change text - jquery (keep original)</button><br />
<button id="btn2">Change text - textContent (keep original)</button><br />
<button id="btn3">Change class (real change)</button>

  • 在Chrome 55中,仅setAttribute() 和 jQuery text()触发了DOM Change事件。
  • 在Firefox 50中,所有的东西都会触发DOM Change事件。
  • 在Edge 38中,所有的东西都会触发DOM Change事件。

2
很棒的回答!我更新了一些结果。不过有一个想法:没有DOM改变事件意味着没有重绘/回流。但是,DOM改变事件是否意味着重绘/回流? - Anders
1
关于repaint/reflow- 我还不确定(仍在搜索),但是这里有一个测试可以尝试 - 取一个带有大量文本的元素(从1M个字符开始,之后可以增加),并使用$(el).text($(el).text())。它将触发DOM,因为它是由jQuery完成的 - 检查浏览器是否需要时间/ CPU /滚动更改来执行此操作。 - Dekel
1
在Firefox中进行了一些测试-发现性能监视器会告诉你何时重新绘制和回流。对于小文本(1K字符),它有时会触发重绘,但不会引起回流。对于大文本(1M字符),每次都会触发昂贵的回流和重绘。 - Anders
很有趣。你同时使用了 jquerytextContent 两种方法吗? - Dekel
1
不,我只使用textContent,因为我无论如何都不想用jQuery。 - Anders

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