如果要监听一个普通 JS 对象的变化,我们会用 Object.defineProperty 或者 Proxy

而监听元素的属性和子节点的变化,我们可以用 MutationObserver:

我第一听说这个方法还是在nextTick 的实现上看到的

由于要模拟在DOM 更新后执行回调函数,所以要将包裹内的同步任务转化为异步任务,又因为兼容性要好,其中一个兼容方案就是MutationObserver。

MutationObserver 可以监听对元素的属性的修改、对它的子节点的增删改。

测试

我们准备这样一个DIV

<div id="box"><button>我是DIV</button></div>

加上样式:

 #box {
    width: 100px;
    height: 100px;
    background: blue;
}

然后我们增加JS操作

setTimeout(() => {
    box.style.background = 'red';
},2000);

setTimeout(() => {
    const dom = document.createElement('button');
    dom.textContent = '我是cheems';
    box.appendChild(dom);
},3000);

setTimeout(() => {
   document.querySelectorAll('button')[0].remove();
},4000);

2s 的时候修改背景颜色为红色

3s 的时候添加一个 button

4s 的时候删除第一个 button。

然后监听它的变化:

const mutationObserver = new MutationObserver((mutationsList) => {
    console.log(mutationsList)
});

mutationObserver.observe(box, {
    attributes: true,
    childList: true
});

可以看到在三次变化的时候都监听到了并打印了信息

然后,这个可以用来做什么呢?

PS:我一开始也觉得没鸟用,因为MVVM都给我绑定好了,我监听值就好了,监听DOM干啥。

它可以监听用户是否主动操作了DOM,那些未绑定的属性如果被操作也可以被监听。这就引出了我下一期内容,防删除水印组件。如果没有就是还在鸽。

但是大家看看先内容监听Hooks与内容监听组件。

MutationObserver的接受细节请点击MDN,自行查看。

内容监听hooks

  1. 支持单个节点/多个节点的监听

  2. 有默认的 options

  3. 在DOM销毁的时候,主动调用 takeRecords 删掉所有剩余通知,调用 disconnect 停止接收新的通知

内容监听组件

  1. 引入内容监听hooks

  2. 通过 React.cloneElement 给 children 加上 ref 来获取dom。

  3. 然后在 useLayoutEffect,调用setState存储ref,触发更新,

  4. 再次渲染的时候,调用 useMutateObserver 就有 dom 了,可以用 MutationObserver 来监听 dom 变化。

over,撤!

未曾清贫难成人,不经打击老天真。 自古英雄出炼狱,从来富贵入凡尘。 醉生梦死谁成气,拓马长枪定乾坤。 挥军千里山河在,立名扬威传后人。