mutationobserver 默认不监听属性变化,必须显式设置 attributes: true 才能捕获 setattribute 等操作;class、style 等通过直接赋值(如 el.classname)修改的是 dom property,非 attribute,故不触发;data-* 属性天然适配。

MutationObserver 默认不监听任何属性变化,必须显式开启 attributes: true 才能捕获 setAttribute、removeAttribute 等操作。
为什么 class 或 style 改了却没触发回调?
因为 el.className = 'new' 或 el.style.color = 'red' 是直接修改 DOM 属性(property),不是 HTML 属性(attribute)——只有通过 setAttribute('class', ...) 或 setAttribute('style', ...) 才会触发 attributes: true 监听。而 data-* 属性天然适配,改它最稳。
- 反射类属性(
class、id、disabled)需用setAttribute触发,直接赋值无效 -
style同理:写el.setAttribute('style', 'color:red')才算 attribute 变更;el.style.color是 property 操作,不进 MutationObserver 队列 -
data-status、data-id这类自定义属性,无论用setAttribute还是dataset.status = 'ok'都会触发(后者底层调用前者)
attributeFilter 和 attributeOldValue 怎么选?
不加 attributeFilter 会监听所有属性变更,可能造成冗余;不设 attributeOldValue: true,回调里拿不到旧值——mutation.oldValue 永远是 undefined。
- 只关心几个关键属性时,用
attributeFilter: ['data-status', 'aria-expanded'],减少无用回调 - 需要比对新旧值(比如判断是否从
hidden切到visible),必须加attributeOldValue: true -
attributeOldValue不影响性能,但没开就真拿不到旧值,且不会报错,容易误以为“没变”
监听失败的三个高频原因
不是 API 写错了,而是目标节点或时机出了问题。
- 传入的
targetNode是null或还没挂载(比如在 Vue 的setup()里直接查document.getElementById,但 DOM 尚未渲染) - 创建了
new MutationObserver(...)却忘了调用observer.observe(target, config) - 试图监听
document或document.body—— 旧版 Safari 不稳定,应改用具体容器(如#app或.content)
监听属性变化时,别漏掉 subtree: true
如果目标节点本身属性不变,但它的子元素被动态插入并带了你要监听的属性(比如框架注入的 data-loaded="true"),仅靠 attributes: true 不够——得打开 subtree: true,否则子节点上的属性变更根本收不到。
-
subtree: true+attributes: true才能捕获后代节点的属性变化 - 若只监听自己身上的属性,
subtree: false(默认)即可,避免过度监听 - 开启
subtree后,attributeFilter依然生效,只过滤匹配的属性名,不扩大监听范围
真正容易被忽略的是:MutationObserver 的回调在 microtask 中执行,你同步修改属性后立刻去查 mutation.oldValue,它一定存在;但如果你在回调里又去改同一个属性,可能触发新一轮观察——循环监听没加防护的话,页面就卡了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











