custom elements 必须满足三条件:标签名含短横线、类继承 htmlelement 并调 super()、注册在 dom 就绪后;命名需小写加单短横线,禁用原生前缀;constructor 仅初始化,渲染放 connectedcallback;注册须等 dom 加载完成;属性监听需 observedattributes 与 attributechangedcallback 配合。

Custom Elements 能直接写进 HTML 文档结构里用,但必须满足三个硬性条件:标签名带短横线、类继承 HTMLElement 且调了 super()、注册时机在 DOM 就绪之后——缺一不可,否则标签会被当成普通未知元素丢进 DOM,后续再注册也救不回来。
自定义标签名为什么必须含短横线?
浏览器靠短横线(-)识别这是自定义元素,不是原生标签。没它,customElements.define() 直接抛 DOMException,不是静默失败。
-
my-button✅ 合法(小写 + 短横线) -
mybutton❌ 报错:Failed to execute 'define' on 'CustomElementRegistry': The name "mybutton" is not a valid custom element name -
MyButton❌ 大写字母触发同上错误 -
div2❌ 名字太像原生标签,被拒绝 -
x--foo❌ 双短横线不合法
命名还要避开保留前缀,比如 html-、svg-、math- 开头的都不行。
constructor 里只做初始化,别渲染 DOM
很多新手在 constructor 里就往 this.innerHTML 写内容或调 document.createElement,这会导致两个问题:一是元素还没挂到文档,DOM 操作可能无效;二是如果用了 attachShadow,又没在 constructor 里调,后续再 attach 会报错。
- ✅ 正确做法:
constructor中只调super()、创建 shadow root(如需)、初始化私有字段 - ✅ 渲染逻辑(读属性、生成结构、加事件监听)统一放
connectedCallback里,此时元素已挂载 - ⚠️ 注意:
connectedCallback可能被多次调用(比如元素被移出又插入),需要自己防重复,比如用this._rendered标记
customElements.define() 执行太早,HTML 里的标签就废了
如果脚本放在 里同步执行,而 <my-card></my-card> 标签在 里,浏览器解析到这个标签时,customElements.define() 还没跑,它就会当普通未知元素处理——哪怕后面注册成功,也不会自动升级,connectedCallback 永远不触发。
- ✅ 最稳妥方式:
document.addEventListener('DOMContentLoaded', () => { customElements.define('my-card', MyCard); }); - ✅ 或者用
<script defer></script>包裹注册代码,确保在 HTML 解析完后执行 - ❌ 千万别写成:
<script>customElements.define('my-card', MyCard);</script>放在顶部 - ⚠️ 动态插入的 script(比如通过
document.createElement('script'))即使加了defer,也不保证执行顺序,别依赖
属性变更监听要配 observedAttributes 和 attributeChangedCallback
光在 connectedCallback 里读一次 this.getAttribute('label') 不够——后续 HTML 里改 label 属性,组件不会响应。得主动监听。
- ✅ 必须声明静态 getter:
static get observedAttributes() { return ['label', 'disabled']; } - ✅ 实现
attributeChangedCallback(attrName, oldValue, newValue),注意:首次设属性时这个回调不触发,初始值还得在connectedCallback或constructor里处理 - ⚠️
attributeChangedCallback的oldValue和newValue都是字符串,布尔属性(如disabled)要手动转类型 - ⚠️ 如果属性值是 JSON 字符串,别在回调里反复
JSON.parse,建议缓存解析结果或用 setter 封装
真正难的不是写个能跑的自定义元素,而是让它的行为和原生元素一致:支持属性反射、可聚焦、键盘可访问、能被 CSS 选择器匹配、属性变更时正确响应。这些细节堆起来,才是封装一个可靠组件的门槛。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











