技术解析

我猜很多人都没真的用过 MutationObserver
0
2021-09-02 04:06:15
idczone

估计看一眼手册知道有这么个东西,然后就再也不用,因为很少写原生 DOM 操作

找了很多帖子,都没有提到这个函数有两个巨坑,我来展示一下


  
    
  
  
    

不用真的运行,推测一下控制台里 div p 和 div a 会出现几次?没用过的话会回答各出现一次。答案是 div p 会出现两次,而 div a 一次都没有

从那些千篇一律的文章中是找不到原因的,他们照着手册写个 demo 就撤了

虽然 MutationObserver 打着替代 DOMNodeInserted,DOMContentLoaded 的旗号,其实工作过程并不一致。对于普通的节点插入,MutationObserver 捕获的节点和 DOMNodeInserted 一样,但是对于整个 html 文档的第一次载入,也就是对应 DOMContentLoaded,MutationObserver 展示出了诡异之处

1 . 例子中的 html 片段,在首次载入时,并不是直接捕获一个 html 节点,而是

捕获 body > div > p

捕获 div > p

捕获 p

你问我是怎么回事?答案是回音,案是回音,是回音,回音,音

MutationObserver 会乐此不彼的重复这个过程,所以 node.querySelector('div p') 出现两次。这个行为和普通的节点插入完全不同

2 . 和 DOMContentLoaded 给你一个 document(注意不是 document.documentElement) 不同,MutationObserver 似乎把整个文档当作流,逐格吐出结果,并且这个流会被打断,也就是 script 标签

实际是这样的过程

注意没有 body > div > a,只有

body > div > p

div > p

p

script:你好打断一下

a <- 没有爹的孩子

所以 node.querySelector('div a') 一次都没有


为了移植一个脚本和这个东西纠缠一天,希望设计这个函数的祭天一下


好像没有人回复时,自己就不能插附言

你应该 mttn.target.querySelector 而不是遍历 addedNodes 中的 node,否则你应该 node.tagName == 'P'。

您说的对。我考虑 mttn.target.querySelector 会去 addedNodes 的兄弟节点查找,是个不必要的性能损失,某些情况会有误判

你要么取交,要么用类似 http://api.jquery.com/is/ 的东西

例子里面的打印不是很有利于观察实际发生的情况,我稍微修改了一下,增加了每次 callback 触发的打印和遍历 added node 时具体的 node 的打印。
```












```
MutationObserver 的核心机制是“异步 + 批量”,主要是处于性能考虑,不过在使用上是容易造成一些误解。改动后的代码打印结果如下:
```
>>> callback here
added element type node ​…​​
[found with selector "div p"]
added element type node
​…​

[found with selector "div p"]
added element type node


added element type node
>>> callback here
added element type node
```
可以看到以下现象:
1. 总共产生了两次 callback
2. 第一次 callback 添加了 body, div, p, script 四个 element node
3. 第二次 callback 添加了 a 这一个 element node
4. 如果你调整 script 标签的位置,你会发现 script 结束会立即触发一次 callback,这可能和 MutationObserver 具体的实现规则有关。
所以实际发生的情况是当第一次 callback 触发时,由于是异步的,所以第一次 callback 内包含的 4 个新增 node 已经存在于 DOM 中,query 'div p' 是可以查询到的,但是 a node 还没有插入,所以此时查询不到。
第二次 callback 时 a node 单独被插入,但是也不符合 'div p' 和 'div a' 的 query 规则,所以不触发打印。
MutationObserver 的异步批量回调机制确实需要比较细致的处理,最近开源的一个项目里也在一段[设计文档]( https://github.com/rrweb-io/rrweb/blob/master/docs/observer.zh_CN.md#%E6%96%B0%E5%A2%9E%E8%8A%82%E7%82%B9)里描述了一些这个机制导致的问题。

排版好像出了些问题,我放在一个 gist 里了
https://gist.github.com/Yuyz0112/005e11735056f0bc992ce821e48647e1

收藏了,感谢各位,学到了点新东西。

数据地带为您的网站提供全球顶级IDC资源