MutationObserver 回调以微任务执行,在主线程空闲后批量处理 DOM 变化,确保高效响应且不阻塞渲染;其执行时机早于 setTimeout 但晚于同轮 Promise.then,且无法获取渲染后尺寸,需用 requestAnimationFrame 获取。

MutationObserver 的回调不是同步执行的,而是作为微任务(microtask)进入队列,在当前 JS 执行栈清空后、下一轮事件循环开始前触发。这种机制既保证了响应及时,又避免阻塞渲染。
微任务入队时机很关键
DOM 变化发生时,浏览器不会立刻调用你的回调。它会先暂存变化,等主线程空闲下来,再把所有待处理变更打包成一个 mutationsList,然后把回调函数作为微任务插入微任务队列末尾。
- 这意味着哪怕你连续执行 10 次
appendChild,也只触发一次回调,且收到的是含 10 条记录的数组 - 如果当前同步代码里有
Promise.resolve().then(),MutationObserver 回调会在这些 Promise 微任务之后执行(微任务队列先进先出) - 它比
setTimeout(宏任务)早得多,但通常不早于同轮注册的Promise.then
回调里拿不到渲染后的 DOM 尺寸
因为微任务在布局和绘制之前执行,此时调用 offsetHeight 或 getBoundingClientRect() 可能返回旧值或 0。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 需要真实渲染结果时,必须用
requestAnimationFrame包裹读取操作 - 例如:
requestAnimationFrame(() => console.log(el.offsetHeight)) - RAF 回调执行前,浏览器已完成样式计算、布局和绘制
回调参数里自带可操作的 observer 实例
回调函数签名是 (mutationsList, observer),第二个参数就是你创建的那个实例,可以直接调用方法:
-
observer.disconnect():立即停止监听,适合只关心首次变化的场景 -
observer.takeRecords():手动提取尚未派发的变更记录,清空内部队列 - 不能在回调里再次调用
observe()监听同一节点,否则可能引发无限递归
配置决定监听什么,但不改变微任务本质
是否启用 childList、attributes 或 subtree,只影响哪些变化会被捕获并汇总进 mutationsList,不影响回调的调度方式。
- 所有匹配的变更仍被批量合并,统一走微任务流程
- 即使只监听一个属性,只要该属性被改了多次,也会等主线程空闲后一次性派发
- 合理设置
attributeFilter或characterDataOldValue能减少无用数据,但不改变微任务排队逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










