mutationobserver 回调以微任务形式入队,dom 变化后批量合并为 mutationslist,在当前同步代码执行完、下一轮事件循环前执行,比 settimeout 早、通常不早于 promise.then;回调接收 mutationslist 和 observer 实例,可调用 disconnect 或 takerecords,但不可在回调内重新 observe;需获取渲染后 dom 状态时应使用 requestanimationframe。

MutationObserver 的回调函数以微任务(microtask)形式入队,不是立即执行,也不走宏任务队列。
回调在微任务队列中排队
当 DOM 变化发生后,浏览器不会立刻调用你的回调函数。它会把这次或多次变化汇总成一个 MutationRecord 列表,等当前 JavaScript 执行栈清空后,把回调作为微任务插入到微任务队列末尾。
- 这意味着它会在当前同步代码执行完、但还没进入下一轮事件循环前被执行
- 比
setTimeout(宏任务)更早执行,但比Promise.then同层级的微任务略晚或并列(取决于注册时机) - 同一轮 DOM 批量操作只会触发一次回调,哪怕中间有十几次
appendChild或setAttribute
变化是批量合并后才触发回调
浏览器会对短时间内的 DOM 修改做防抖式聚合:只要主线程没空下来,变化就暂存;一旦空闲,就把所有待处理变更打包进一个 mutationsList 传给回调。
- 例如连续添加 5 个子节点,回调里收到的
mutationsList是一个含 5 条记录的数组,而不是触发 5 次回调 - 这种机制避免了高频触发带来的性能问题
- 如果中间穿插了
Promise.resolve().then(),回调仍会等 Promise 微任务之后再执行(微任务队列先进先出)
回调参数里的 observer 是可操作实例
回调函数签名是 (mutationsList, observer),第二个参数就是你创建的 MutationObserver 实例本身。
- 可以在回调里直接调用
observer.disconnect()停止监听(比如只关心首次变动) - 也可调用
observer.takeRecords()提前清空队列,获取尚未派发的变更记录 - 注意:不能在回调里再次调用
observe()监听同一个节点,否则可能引发无限递归
想读取最新 DOM 状态?得用 requestAnimationFrame
由于回调是微任务,此时样式计算和布局尚未完成,直接查 offsetHeight 或 getBoundingClientRect() 可能拿不到准确值。
- 需要 DOM 渲染结果时,应包裹在
requestAnimationFrame里 - 例如:
requestAnimationFrame(() => console.log(el.offsetHeight)) - 这是因为在 RAF 回调执行前,浏览器已完成样式计算、布局和绘制











