-
作用
MutationObserver 接口提供了监视对 DOM 树所有变化(包含节点、属性、文本内容的变动)的能力。
-
微任务
和EventTargetAPI的addEventListener相比:
共同点: 两者的回调函数执行都是异步任务,都要在主执行栈之后执行;
不同点:
-
addEventListener的回调函数会进入宏任务队列;MutationObserver的回调函数会进入微任务队列;
-
addEventListener的触发方式是同步触发;比如,点击后,回调函数立即进入宏任务队列。MutationObserver的监听是异步触发,在所有的DOM操作完成后才触发使回调函数进入微任务队列。
比如当前有几个对DOM的操作,MutationObserver的回调函数必须要等到最后一个操作结束才会进入微任务队列。
微任务在dom渲染之前执行,宏任务在dom渲染之后执行。
-
使用
var MutationObserver: new (callback: MutationCallback) => MutationObserver
interface MutationObserver {
// 阻止 MutationObserver 实例继续接收的通知,直到再次调用其 observe() 方法,该观察者对象包含的回调函数都不会再被调用
disconnect(): void;
// 配置 MutationObserver 在 DOM 更改匹配给定选项时,通过其回调函数开始接收通知。
observe(target: Node, options?: MutationObserverInit): void;Ï
// 从 MutationObserver 的通知队列中删除所有待处理的通知,并将它们返回到 MutationRecord 对象的新 Array 中。
takeRecords(): MutationRecord[];
}
interface MutationObserverInit {
// 一个用于声明哪些属性名会被监听的数组。如果不声明该属性,所有属性的变化都将触发通知。
attributeFilter?: string[];
// 当为 true 时,记录上一次被监听的节点的属性变化;可查阅监听属性值了解关于观察属性变化和属性值记录的详情。默认值为 false。
attributeOldValue?: boolean;
// 当为 true 时观察所有监听的节点属性值的变化。默认值为 true,当声明了 attributeFilter 或 attributeOldValue,默认值则为 false。
attributes?: boolean;
// 当为 true 时,监听声明的 target 节点上所有字符的变化。默认值为 true,如果声明了 characterDataOldValue,默认值则为 false
characterData?: boolean;
// 当为 true 时,记录前一个被监听的节点中发生的文本变化。默认值为 false
characterDataOldValue?: boolean;
// 当为 true 时,监听 target 节点中发生的节点的新增与删除(同时,如果 subtree 为 true,会针对整个子树生效)。默认值为 false。
childList?: boolean;
// 当为 true 时,将会监听以 target 为根节点的整个子树。包括子树中所有节点的属性,而不仅仅是针对 target。默认值为 false
subtree?: boolean;
}
举一个例子
function ExampleComponent(props){
...
const domId = useRef('');
const treeContentDivRef = useRef(null);
/**
* 这里用MutationObserver是因为select选中的节点可能处于某个折叠的父节点中,
* 那么此时需要等到父节点,才会生成该节点,
* 如果不通过MutationObserver,则会导致该情况无法顺利滚动到该节点
*/
const observer = new MutationObserver(() => {
if (quickSearchSelectKeyRef.current) {
const anchor = document.getElementById(domId.current);
if (anchor) {
// MutationObserver的回调函数是属于微任务,
// 微任务在dom渲染之前执行,所以会导致滚动触发不那么正确
setTimeout(() => {
anchor.scrollIntoView({
behavior: 'smooth',
});
quickSearchSelectKeyRef.current = '';
});
}
}
});
useEffect(() => {
if (treeContentDivRef.current) {
observer.observe(treeContentDivRef.current, { attributes: true, childList: true, subtree: true });
return () => {
observer.disconnect();
};
}
}, []);
...
return <Tree ref={treeContentDivRef} />
}
这里列了部分关键代码,该功能是我通过一个Select选中了Tree中的某个节点,如果选中后直接scroll过去,可能会因为该节点的父节点还未渲染出展开状态,导致该节点还没渲染出来,scroll会不成功。所以加了个MutationObserver,监听到Tree发生变化之后,再进行scroll。