Javascript MutationObserver检测整个DOM中节点的性能

Javascript MutationObserver检测整个DOM中节点的性能,javascript,html,mutation-observers,Javascript,Html,Mutation Observers,我感兴趣的是使用MutationObserver来检测HTML页面中是否添加了特定的HTML元素。例如,我要说的是,我想检测DOM中是否添加了任何 到目前为止,我看到的所有MutationObserver示例都只检测节点是否添加到特定容器中。例如: 一些HTML <body> ... <ul id='my-list'></ul> ... </body> 因此,在本例中,MutationObserver被设置为监视一个非常特定的

我感兴趣的是使用
MutationObserver
来检测HTML页面中是否添加了特定的HTML元素。例如,我要说的是,我想检测DOM中是否添加了任何
  • 到目前为止,我看到的所有
    MutationObserver
    示例都只检测节点是否添加到特定容器中。例如:

    一些HTML

    <body>
    
      ...
    
      <ul id='my-list'></ul>
    
      ...
    
    </body>
    
    因此,在本例中,
    MutationObserver
    被设置为监视一个非常特定的容器(
    ul#my list
    ),以查看是否有任何
  • 附加到它

    如果我想不那么具体,并且像这样在整个HTML正文中观察
  • ,这是一个问题吗:

    var container = document.querySelector('body');
    
    我知道它在我为自己设置的基本示例中有效。。。但不建议这样做吗?这是否会导致性能不佳?如果是这样,我将如何检测和衡量性能问题

    我想可能是因为所有的
    MutationObserver
    示例都非常特定于它们的目标容器。。。但我不确定。

    这个答案主要适用于大而复杂的页面。 如果在页面加载/呈现之前附加,如果页面很大且复杂(,),则未优化的MutationObserver回调可以为页面加载时间增加几秒钟(例如,5秒到7秒)。回调作为一个微任务执行,它会阻止DOM的进一步处理,并且在一个复杂的页面上每秒可以触发数百次或数千次。大多数示例和现有库都没有考虑到这种情况,并且提供了美观、易于使用但可能速度较慢的JS代码

  • 始终使用,并尝试使您的观察者回调占用的CPU时间少于页面加载期间总CPU时间的1%

  • 通过访问offsetTop和类似属性避免触发

  • 避免使用jQuery之类的复杂DOM框架/库,更喜欢原生DOM

  • 观察属性时,请使用
    .observe()中的
    属性过滤器:[attr1',attr2']
    选项

  • 尽可能不重复地观察直系父母(
    子树:false
    )。
    例如,通过递归观察
    document
    来等待父元素是有意义的,成功时断开观察者的连接,在此容器元素上附加一个新的非递归元素

  • 当只等待一个具有
    id
    属性的元素时,使用异常快速的
    getElementById
    而不是枚举
    突变
    数组(它可能有数千个条目):

  • 如果所需元素在页面上相对较少(例如,
    iframe
    object
    ),请使用由
    getElementsByTagName
    getElementsByClassName
    返回的live HTMLCollection,并重新检查所有元素,而不是枚举
    突变
    ,如果它有100多个元素,比如说

  • 避免使用
    querySelector
    ,尤其是速度极慢的
    querySelector all

  • 如果
    querySelectorAll
    在MutationObserver回调中是绝对不可避免的,首先执行
    querySelector
    检查,如果成功,继续执行
    querySelectorAll
    。平均来说,这样的组合会快得多

  • 如果针对2018年之前的Chrome/ium,不要使用需要回调的内置数组方法,如forEach、filter等,因为在Chrome的V8中,调用这些函数总是比调用经典的
    for(var i=0…)
    loop(慢10-100倍),而MutationObserver回调可能会在复杂的现代页面上报告数千个节点

    • 即使在较旧的浏览器中,lodash或类似的fast库支持的替代函数枚举也可以
    • 自2018年起,Chrome/ium内置了标准阵列方法
  • 如果针对2019年之前的浏览器,不要在MutationObserver回调中使用like
    for(让v of something)
    ,除非您进行了传输,以便生成的代码与经典的
    for
    循环一样快

  • 如果目标是改变页面外观,并且您有一种可靠且快速的方法来判断添加的元素是否在页面的可见部分之外,请断开观察者的连接,并通过
    setTimeout(fn,0)安排整个页面的重新检查和重新处理
    :当解析/布局活动的初始突发完成时,引擎会“呼吸”,这可能需要一秒钟的时间。然后,您可以使用requestAnimationFrame不显眼地将页面分块处理

  • 如果处理很复杂和/或需要大量时间,则可能会导致很长的画框、无响应/抖动,因此在这种情况下,您可以使用去抖动或类似技术,例如,在外部数组中累积突变,并通过setTimeout/requestIdleCallback/requestAnimationFrame安排运行:

    const queue=[];
    const mo=新的突变观察者(突变=>{
    如果(!queue.length)请求AnimationFrame(进程);
    排队推送(突变);
    });
    函数过程(){
    for(队列的常数突变){
    // ..........
    }
    queue.length=0;
    }
    
  • 回到问题上来: 观察一个非常特定的容器
    ul#my list
    ,查看是否有任何
  • 附加到它

    由于
    li
    是一个直接的子节点,我们需要寻找添加的节点,因此唯一需要的选项是
    childList:true
    (请参见上面的建议2)


    性能问题是特定于给定场景的。如果您的整个文档只有几个简单的元素,我相信您不会有任何问题。如果您担心出现性能问题,请配置文件!我使用了大量的
    MutationObserver
    s,让它们递归地监视整个DOM。我个人从来没有
    var container = document.querySelector('body');
    
    new MutationObserver(function(mutations, observer) {
        // Do something here
    
        // Stop observing if needed:
        observer.disconnect();
    }).observe(document.querySelector('ul#my-list'), {childList: true});