attributeChangedCallback 不会触发 HTML 初始属性赋值,需在 connectedCallback 中手动读取;constructor 中不可读属性或操作 DOM;data-* 属性不应作组件配置;纯数字 ID 会导致 getElementById 失效。

attributeChangedCallback 不会读到 HTML 里写的初始值
这是最常踩的坑:在 customElements.define() 注册后,<my-input value="hello"></my-input> 中的 value="hello" 不会触发 attributeChangedCallback。它只响应后续 JS 调用 setAttribute() 或属性被 JS 修改的变更,不处理 HTML 解析阶段的初始赋值。
原因在于浏览器解析 HTML 时,先创建元素、挂载、再设置属性;而 attributeChangedCallback 是在属性“变化”时才调用,初始设置不算“变化”。
- 必须在
connectedCallback中手动读取一次初始属性,例如:this.value = this.getAttribute('value') || '' - 如果用了
static get observedAttributes(),记得把要监听的属性名(如'value')列进去,否则连后续变更也不会响应 - 若属性值是布尔型(如
disabled),注意getAttribute('disabled')返回字符串"disabled"或null,不能直接当 boolean 用
constructor 里不能读属性,但可以提前准备 shadow root
constructor 是元素实例化第一站,但它运行时 DOM 还没挂载,属性虽已存在,但调用 this.getAttribute() 可能返回 null(尤其在 SSR 或某些 polyfill 环境下不稳定),更别说操作子节点或渲染了。
安全做法是只做三件事:
立即学习“前端免费学习笔记(深入)”;
- 必须调
super() - 用
this.attachShadow({ mode: 'open' })创建 shadow root(别等connectedCallback再做,否则样式注入可能延迟) - 初始化私有字段或绑定事件处理器(如
this._onClick = this._onClick.bind(this))
所有属性读取、模板填充、DOM 渲染,一律挪到 connectedCallback 里做——这时元素已插入文档,属性可稳定读取,shadow root 也已就位。
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
data-* 属性不适合传给自定义元素作初始配置
如果你试图用 <my-chart data-api="/v1/chart" data-theme="dark"></my-chart>,这其实违反语义:data-* 是为宿主页面存元数据设计的,不是组件 API 的一部分。自定义元素该用标准属性(如 api、theme)暴露接口,再靠 observedAttributes + attributeChangedCallback 响应。
混用 data-* 会导致两个问题:
- 无法被
observedAttributes监听(浏览器不认为它是“可观察属性”) - 破坏组件封装性:外部脚本可能误改
data-api却不触发更新逻辑 - IDE 和类型系统(如 TypeScript)无法推导属性签名,维护成本上升
真要传结构化配置(比如带嵌套对象),建议统一走一个 config 属性:<my-table config='{"columns": ["id", "name"], "page": 1}'></my-table>,然后在 connectedCallback 里 JSON.parse(this.getAttribute('config') || '{}') —— 注意加 try/catch 防止 JSON 错误。
数字 ID 导致 document.getElementById() 失败会影响自定义元素初始化
如果自定义元素的宿主容器 ID 是纯数字(比如 <div id="123"><my-widget></my-widget></div>),后续用 document.getElementById('123') 获取该容器会返回 null。这不是自定义元素的问题,而是 HTML 规范限制:ID 属性值虽允许数字开头,但 JS 的 getElementById 在部分浏览器(尤其是旧版 Safari 和某些 polyfill)中会把它当作非法标识符忽略。
解决方案很简单但容易被忽略:
- 避免用纯数字做 ID,改用
widget-123、item_123等带前缀/下划线的形式 - 如果必须兼容遗留 ID,改用
document.querySelector('[id="123"]')—— 它不依赖标识符合法性,100% 可靠 - 在自定义元素内部,不要假设父容器 ID 可通过
getElementById拿到;优先用this.parentElement或this.closest()向上查找
初始属性读取这事,表面是 API 调用顺序问题,实际卡点往往在 DOM 生命周期认知偏差和 HTML 边界行为上。多一次 connectedCallback 里的手动同步,比后期 debug 属性“突然不生效”省半小时。


















