attributeChangedCallback未调用的根本原因是未声明observedAttributes;它只响应setAttribute等attribute操作,不响应property赋值;首次触发时oldValue为null,newValue恒为字符串。

attributeChangedCallback 为什么没调用
根本原因就一个:没声明 observedAttributes。浏览器不会自动监听任何属性,必须显式告诉它“我要盯哪几个”。这个静态 getter 必须返回字符串数组,且每个字符串要和 HTML 中写的属性名**逐字符一致**——包括大小写、连字符、data-前缀。比如 HTML 写了 <my-card data-user-id="123">,就得写 return ['data-user-id'];写成 ['datasetuserid'] 或 ['userId'] 都无效。
只响应 attribute 操作,不响应 property 赋值
attributeChangedCallback 只在以下场景触发:
el.setAttribute('disabled', 'true')el.removeAttribute('disabled')- HTML 解析时的初始属性(如
<my-input value="init">)
但它完全无视 el.disabled = true 这类 property 操作。这是 attribute 和 property 的本质区别,不是 bug。如果你从 React/Vue 传值,它们内部通常走 setAttribute,所以能触发;但手写 JS 直接赋值,就不会进回调。
首次设置时 oldValue 是 null,不是 undefined 或空字符串
回调签名是 attributeChangedCallback(attrName, oldValue, newValue),首次出现该属性时,oldValue 一定是 null。常见错误是写 if (!oldValue) 或 oldValue === undefined 来判断是否首次,结果首次就被跳过或误判。正确做法是:
立即学习“前端免费学习笔记(深入)”;
if (oldValue === null) {
// 初始值逻辑,比如同步到 this._value
} else if (oldValue !== newValue) {
// 真正的变化逻辑
}
另外,newValue 永远是字符串,哪怕你在 HTML 里写 count=42,拿到的也是 "42";需要数字就自己 Number(newValue),但得先判断 newValue !== null,否则 Number(null) 得到 0,不是你想要的。
别在回调里做 DOM 操作或发起请求
attributeChangedCallback 是纯响应函数,适合做状态同步、类型转换、简单校验。但:
- 不要在里面改
this.innerHTML或直接操作document—— 用 Shadow DOM 的话,应统一走this.shadowRoot - 避免重复更新:连续两次
setAttribute('count', '5')会触发两次回调,记得加oldValue !== newValue判断 - 别启动定时器、发 fetch、绑定事件 —— 这些该放在
connectedCallback里一次性处理
最常被忽略的是:这个回调里不能用 async/await,但可以 fetch(...).then(...);constructor 里也不能碰 DOM,因为此时 this.shadowRoot 还是 null。



















