fwon / blog

writing thinking
286 stars 27 forks source link

MutationObserver用法浅析 #5

Open fwon opened 10 years ago

fwon commented 10 years ago

最近看了fex团队的一篇关于前端xss攻击的文章,感觉非常精彩。 里面对于MutationEvent的运用让人眼前一亮。所以顺便学习并记录了一下该事件的相关用法。 Mutation events 包括DOMNodeInserted事件,其用法如下:

document.addEventListener('DOMNodeInserted', function(e) {
    console.log('DOMNodeInserted:', e);
}, true);

var el = document.createElement('script');
el.src = 'http://www.xxx.com/xss/out.js?dynamic';
document.body.appendChild(el);

当程序动态往document添加script节点时,MutationEvent捕抓到了该DOMNodeInserted事件,并触发了回调,输出‘DOMNodeInserted:xxx’。

类似的事件还有:

但是使用MotationEvent有两个缺点: 一是会影响DOM操作的执行效率,可能会有高达1.5-7倍的延迟!而且移除监听器的操作,也会带来性能上的影响。 二是会存在跨浏览器兼容的问题。 对于第二点我们都知道是因为各浏览器厂商对API支持的不一致导致的,那为什么会存在第一个问题呢?个人认为有以下原因。 第一,对DOM节点的监听会消耗节点遍历的时间,而被监听DOM的分支越深,则所用时间越多。 第二,某些事件如DOMAttributeNameChanged and DOMElementNameChanged.等会储存节点信息,而当这些信息为SVG attribute或一些大的样式,其开销也会相当大。mutation将这些信息封装为一个信息量很大的objects并返回给回调函数,这些操作需要一定的计算消耗和内存损耗。

因为mutation event存在比较大的性能问题,它已经逐渐被淘汰了,而且官方建议最好不再使用。取而代之的是MutationObserver类,其构造函数的格式为:

MutationObserver(
     function callback
);

callback函数有两个参数,第一个参数为MutationRecord类型的对象,第二个为该MutationObserver的实例。每一个MutationObserver包含三个方法。

方法一, observe(Node target, MutationObserverInit options); observe即为监听函数,传入两个参数:

void observe(
     Node target,     //设置监听的节点
     MutationObserverInit options     //设置监听的选项
);

一个最简单的监听是这样写的:

var observer = new MutationObserver(function(mutations) {
    console.log('MutationObserver:', mutations);
});
observer.observe(document, {
    subtree: true, //表示对target的后代也添加该监听
    childList: true //必选项,表示子节点(包括文字节点)的添加删除操作会被监听
});

当html中外部引入js时

<script src="http://www.xxx.com/xss/out.js"></script>

MutationObserver监听到了document中的静态元素变化。发现有新元素添加就会触发回调。当页面中载入时,会输出mutations对象。 方法二,disconnect() 取消节点的监听事件。 方法三,takeRecords() 清除MutationObserver事例的记录队列并返回该队列。 返回的类型为MutationRecords,也就是MutationObserver构造函数回调的第一个参数。 该object包括addedNodes,removedNodes等节点信息。 虽然运用这些方法虽然比较爽地解决一些问题,但是基于节点的遍历在性能上还是存在一定瓶颈,这在于这种观察者模式的内部实现,虽然MutationObserser已经做了优化,但是个人建议非必要时还是不要滥用。

参考: 1.https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events 2.https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

hammerwang666 commented 9 years ago

MutationObserver 受教了

xiaokaike commented 8 years ago

Vue 的异步更新dom也是通过 MutationObserver 实现的

https://github.com/vuejs/vue/blob/dev/src/util/env.js#L79

fwon commented 8 years ago

@xiaokaike 受教了