mutationobserver 通过微任务聚合 dom 变动,同一宏任务内的所有变更合并为一次回调,传入有序 mutationrecord 数组;记录同步写入、异步派发,回调中 dom 为最终态快照,需用 requestanimationframe 获取布局信息,合理配置 filter 和观察范围可提升性能。

MutationObserver 在处理大量 DOM 节点变动时,并不逐个响应,而是靠浏览器内建的微任务调度机制自动聚合——所有在同一个宏任务中发生的变动,会被合并进一次回调,传入一个结构化的 MutationRecord 数组。
变动记录是同步写入、异步派发
每次调用 appendChild、removeChild、setAttribute 等操作,浏览器内核会立即生成一条变更记录,写入内部队列。这个过程是轻量且同步的,不触发回调,也不打断当前脚本执行。
- 即使连续插入 100 个节点,也只产生 100 条记录,但不会触发 100 次回调
- 记录按发生顺序排列,但打包时机由事件循环决定:等当前宏任务(如点击事件、
setTimeout回调)彻底结束 - 随后浏览器清空微任务队列,统一触发
MutationObserver回调
回调中拿到的是“最终态快照”,不是中间过程
你在回调里看到的 DOM,是这一轮 JS 执行后所有变动完成后的结果,但尚未渲染上屏。这让你能安全地做批量判断,比如检查是否整批内容已就位,再启用滚动定位或样式计算。
-
mutations数组里可能同时包含addedNodes、removedNodes和attributes变更 - 同一节点的多次 class 修改,常被合并为一条
attributes记录,附带oldValue和新值 - 若需获取布局信息(如高度、位置),不能在回调里直接读
offsetHeight,否则触发强制重排;应改用requestAnimationFrame
性能控制依赖配置收口,而非回调内手动节流
真正影响吞吐量的,不是回调执行多快,而是有多少变动被放进队列。靠合理配置,可以从源头减少无效记录生成。
- 用
attributeFilter: ['class', 'data-loaded']锁定关键属性,忽略style或随机data-属性变更 - 观察范围尽量具体,避免监听
document或body;高频区域可拆分为多个 observer 分责 - 不再需要时及时调用
observer.disconnect(),防止长期积累记录导致内存压力
和 Vue/React 等框架协同更高效
现代框架的更新本身已是批量的。MutationObserver 的微任务时机,天然落在框架 patch 完成之后、渲染之前,因此你监听到的是稳定、完整的 DOM 结构,而非组件重渲染过程中的中间态。
- 例如广告 SDK 注入多个
<script></script>和<div>,你只需一次遍历识别并拦截 <li>富文本编辑器监听 <code>textContent+ 关键属性,可忽略 layout 触发的无关style变动 - 不建议在回调里再触发新的 DOM 写入(如循环
classList.add),应汇总后一次性应用,或使用DocumentFragment











