<p>observedAttributes 必须返回小写连字符格式的字符串数组,且拼写与HTML中属性名完全一致;仅响应 setAttribute() 和初始HTML属性变更,不响应 property 赋值;data-* 属性需显式声明并用 setAttribute 修改;首次触发 oldValue 为 null;需手动调用第三方库更新接口。</p>

observedAttributes 必须返回小写连字符格式的字符串数组
浏览器只响应你在 observedAttributes 里**显式声明且拼写完全一致**的属性名。比如 HTML 中写的是 <my-input data-user-id="123">,那必须写 return ['data-user-id'];写成 ['datasetuserid']、['userId'] 或 ['dataUserId'] 都不会触发 attributeChangedCallback。
常见错误现象:属性明明改了,回调一次都没进。
- HTML 属性名含大写字母(如
Label),但observedAttributes写的是['label']→ 不触发 - 用了
data-前缀却漏掉它(比如 HTML 是data-config,代码里只写['config'])→ 不触发 - 数组里混入不存在的属性名(如多写了个
'disabled',但你的元素根本不支持该 attribute)→ 不报错,但白监听
attributeChangedCallback 只响应 setAttribute() 和初始 HTML 属性
attributeChangedCallback 的触发前提是 DOM attribute 被修改,不是 JavaScript property 赋值。你写 el.dataId = 'abc' 或 el.dataset.id = 'abc',哪怕 data-id 已在 observedAttributes 里,也绝不会进回调。
使用场景很明确:外部框架(React/Vue)传 props、手动调用 setAttribute()、或者元素初次解析时带属性(如 <my-toggle checked>)。
立即学习“前端免费学习笔记(深入)”;
- 首次渲染时,
oldValue一定是null,不是undefined或空字符串 —— 别用!oldValue判断是否首次 - 连续两次
el.setAttribute('count', '5')会触发两次回调,建议开头加if (oldValue === newValue) return - 对
newValue做类型转换前,先判断是否为null(比如JSON.parse(newValue)会炸)
data-* 属性要监听就得单独列进 observedAttributes
浏览器不会自动监听任何 data-* 属性,哪怕你用了 dataset API。想让 data-config 或 data-series 触发回调,就必须把它写进 observedAttributes 数组,并且确保后续只用 setAttribute('data-config', JSON.stringify(obj)) 修改。
注意:el.dataset.config = 'xxx' 不会触发回调,因为这等价于操作 el.setAttribute('data-config', 'xxx') 的语法糖,但某些旧版浏览器或严格模式下行为不一致,**务必统一用 setAttribute**。
-
data-属性值永远是字符串,解析 JSON 或转数字得自己做,别指望浏览器帮你 - 如果属性值常变且结构复杂(如嵌套对象),建议只存 ID 或 key,在回调里查表映射,避免反复
JSON.parse - 不要在
connectedCallback里重新定义observedAttributes—— 它是类静态属性,注册后就固定了
第三方库集成时,属性变更必须主动调用更新方法
自定义元素监听到 data-options 变了,不代表第三方实例(Chart.js、Leaflet 等)会自动重绘。你得在 attributeChangedCallback 里手动调用它的更新接口,比如 this._chart?.update() 或 this._map?.setOptions(...)。
容易踩的坑是只改了 DOM 或重写了 innerHTML,却忘了驱动底层库。
- 首次初始化用
new Chart(ctx, config),后续变更优先走.data = ...; .update(),别每次都 new - 地图类组件(Leaflet/Mapbox)必须手动
this._map.remove()清理旧实例,否则内存泄漏 - 如果用了 Shadow DOM,Chart.js 之类依赖 CSS 尺寸和父级定位的库大概率失败 —— 改用 light DOM 或
<slot>暴露容器
真正麻烦的不是写对 observedAttributes,而是搞清哪些变更属于 attribute、哪些属于 property,以及第三方库到底吃哪一套更新方式。漏掉任意一环,看起来“属性变了”,界面就是不动。



















