mutationobserver 需 target(已挂载的 dom 元素)、options(至少启用 childlist/attributes/characterdata)和 observe() 三者齐全才能生效;回调中 mutationrecord 是静态快照,需注意节点状态、布局读取时机及无限循环风险;务必及时 disconnect 释放资源。

MutationObserver 不是“开了就能用”的监听器,必须 target、options、observe() 三者齐全才可能触发回调;漏掉任意一环,监听就完全失效。
目标节点必须真实挂载
target 必须是已插入文档的 DOM 元素,不能是 null、undefined、DocumentFragment 或尚未渲染的 Vue/React 节点。常见错误是在组件 setup 阶段直接查 ID,但此时元素还没挂载。
- 写法前加 console.log(target, target?.nodeType) 确认输出类似
<div id="app">...</div>和 nodeType 为 1 - 动态创建的节点(如
document.createElement('div'))要先appendChild()或insertAdjacentElement()到真实 DOM,再调用observe() - 避免监听
document或document.body—— Safari 等旧浏览器支持不稳定,优先选具体容器(如#app、.content)
配置项至少启用一种变化类型
MutationObserver 默认不监听任何变更,options 对象中 childList、attributes、characterData 至少一个需为 true,否则会抛出 TypeError。
- 监听子节点增删 →
{ childList: true };若需捕获深层嵌套变化,必须加subtree: true - 监听 class/style 变更 → 不能只改
el.className或el.style.color,得用setAttribute('class', ...)并配{ attributes: true, attributeFilter: ['class'] } - 监听文本内容 → 必须同时设
{ characterData: true, subtree: true };仅characterData: true只响应目标节点自身 textNode
回调中注意数据时效性与副作用
MutationRecord 是变更发生那一刻的静态快照,不是活引用。被移除的节点已脱离文档树,新增节点也可能尚未完成样式计算。
-
mutation.removedNodes中的节点调.querySelector()或读.offsetHeight会失败 → 先用Array.from(mutation.removedNodes)缓存副本 - 需要读布局信息(如
getBoundingClientRect())→ 放进requestAnimationFrame()或setTimeout(() => {}, 0)再操作 - 回调中直接修改同一节点文本可能引发无限循环 → 加标记位控制(如
if (isHandling) return),或用takeRecords()清空队列后再处理
及时断开与框架兼容要点
监听器长期存在会占用内存,且在 Vue 的 v-if、React 的 key 变更等场景下,旧节点被彻底销毁,observer 容易断连或漏事件。
- 不再需要时务必调用
observer.disconnect()释放资源 - 在框架生命周期钩子中管理:Vue 可在
onUnmounted断开,React 可在useEffect清理函数中调用 - 若监听区域频繁重建,考虑监听父容器 +
subtree: true,而非具体子节点 - 与 Vue 响应式联动时,可配合
nextTick启动观察,确保 DOM 批量更新完成后开始监听











