自定义元素需继承HTMLElement且名称含短横线,通过customElements.define()注册;constructor初始化属性,connectedCallback在插入文档时触发但DOM未就绪,attributeChangedCallback捕获声明属性的变化,disconnectedCallback用于清理。

自定义元素(Custom Elements)不是“需要教程才能用”的黑箱,而是浏览器原生支持的 Web Components 核心能力;关键在于理解 customElements.define() 的约束和生命周期回调的真实触发时机——错把 connectedCallback 当作“渲染完成”是绝大多数人踩坑的起点。
如何正确注册一个自定义元素
必须继承 HTMLElement,且类名中必须含短横线(-),否则浏览器直接抛 DOMException: Failed to execute 'define' on 'CustomElementRegistry': The name "mybutton" is not a valid custom element name。不能用纯字母命名,也不能用已知 HTML 标签名(如 div、section)。
注册前必须确保类已声明,且不能重复注册同名元素(会报 NotSupportedError):
class MyCounter extends HTMLElement {
constructor() {
super(); // 必须调用
this.count = 0;
}
}
customElements.define('my-counter', MyCounter); // ✅ 合法名称
// customElements.define('mycounter', MyCounter); // ❌ 报错
四个标准生命周期回调分别在什么时候触发
它们不是按“创建→插入→更新→移除”顺序线性执行,也不保证 DOM 已就绪或样式已计算。每个回调只负责它明确承诺的事:
立即学习“Java免费学习笔记(深入)”;
-
constructor():仅用于初始化实例属性、绑定方法、创建 Shadow DOM(此时不能访问this.shadowRoot或this.parentNode,更不能操作子节点) -
connectedCallback():元素被插入任意文档(包括document.adoptNode()或innerHTML插入)时触发;但此时子元素可能尚未 upgrade,且 CSS 可能未生效 -
disconnectedCallback():元素从文档中移除时触发;适合清理事件监听器、定时器;不保证该元素之后不会被重新插入 -
attributeChangedCallback(attrName, oldValue, newValue):仅当该属性在observedAttributes静态 getter 中声明过,且通过setAttribute()或属性赋值(如el.foo = 'bar')变更时才触发;初始属性值(HTML 中写的)也会触发一次,但 oldValue 是null
为什么 connectedCallback 里拿不到子节点或样式?
因为它是同步触发的,早于浏览器布局计算和子组件 upgrade。常见误操作是:
connectedCallback() {
console.log(this.children.length); // 很可能为 0,即使 HTML 写了 1
console.log(getComputedStyle(this).color); // 可能是空字符串或默认值
}
若需等子节点就绪,可用 requestAnimationFrame() 延迟一帧(适用于简单场景),或监听 slotchange 事件(适用于含 的 Shadow DOM);若需等所有自定义子元素 upgrade 完成,应使用 customElements.whenDefined('xxx') + Promise.all() 组合判断。
升级(upgrade)机制容易被忽略的关键点
已存在 DOM 中的元素,在 customElements.define() 调用后会立即 upgrade(即执行 constructor 和 connectedCallback)。这意味着:
- HTML 中先写
,再加载并 define 类 ——count属性会在 upgrade 时触发attributeChangedCallback - 如果在
constructor里读this.getAttribute('count'),会得到null(因为 attribute 还没被解析进属性),必须靠attributeChangedCallback捕获 - Shadow DOM 中的
默认是异步分发的,slot.assignedNodes()在connectedCallback里调用仍可能为空
真正可靠的“组件完全就绪”信号,往往得自己组合:upgrade + attribute change + slotchange + rAF,没有银弹。











