自定义元素名必须含短横线,否则customElements.define()立即抛DOMException;需继承HTMLElement、注册早于DOM渲染、用connectedCallback而非constructor操作DOM,并通过formAssociated和attachInternals实现表单集成。

直接用 customElements.define() 注册一个继承 HTMLElement 的类,就能得到可重用的 UI 组件——但多数人卡在命名、生命周期误用或表单集成失败上,不是技术不可行,而是细节没对齐浏览器规范。
自定义元素名为什么必须带短横线
浏览器靠短横线(-)区分原生标签和自定义标签。写成 <mybutton></mybutton> 或 <mybutton></mybutton> 都会报错:Failed to execute 'define' on 'CustomElementRegistry': The name provided ("mybutton") is not a valid custom element name。
- 合法名:只能小写字母 + 数字 + 短横线,且必须含至少一个短横线,如
my-button、data-table、x-slider - 非法名:不含短横线(
mybutton)、首字符是数字(1panel)、含大写字母(MyButton)、含下划线(my_button) - 影响:名字不合法 →
customElements.define()抛错,组件根本不会注册,HTML 中出现该标签也不会触发任何生命周期
constructor 和 connectedCallback 该放什么逻辑
构造函数(constructor)只做同步初始化;真正挂载 DOM、绑定事件、读取属性,得等 connectedCallback ——因为此时元素才被插入文档,this.innerHTML 或 this.querySelector 才有效。
-
constructor:必须调用super();可创建shadowRoot、声明私有变量(如this._value = null)、设置默认状态 -
connectedCallback:渲染模板、监听子节点事件、读取属性值(this.getAttribute('disabled'))、调用this.internals.setFormValue()(如果关联表单) - 常见错误:在
constructor里调用this.querySelector→ 返回null;在connectedCallback重复添加事件监听器 → 多次触发
让自定义表单控件真正参与提交的关键三步
光渲染一个输入框外观没用,form.elements 里找不到它,submit 事件也收不到它的值——除非显式启用表单关联能力。
立即学习“前端免费学习笔记(深入)”;
- 声明
formAssociated: true:注册时传入第三个参数,否则this.attachInternals()会抛NotSupportedError - 调用
this.attachInternals():必须在constructor中执行,返回ElementInternals实例 - 主动同步值:在
connectedCallback中监听内部<input>的input或change事件,再调用this.internals.setFormValue(value) - 额外注意:不能继承
HTMLInputElement,Chrome 100+ 已禁用extends="input",Firefox/Safari 基本不支持,只能走自治型路径
Shadow DOM mode: open 还是 closed
选 open 是工程实践中的默认选择;closed 几乎无实用场景,反而增加调试成本。
-
open:可通过el.shadowRoot直接访问影子根,方便 DevTools 检查、CSS 覆盖(比如父级加my-button::part(label) { color: red })、自动化测试查询 -
closed:el.shadowRoot返回null,完全屏蔽内部结构,连你自己都难调试,仅适用于极少数强封装需求(如密码输入控件防 DOM 窥探) - 性能影响:两者开销几乎一致,差异不在运行时,而在可维护性——
closed让你失去所有调试入口,出问题只能靠 console.log 推测
最易被忽略的是:自定义元素一旦注册,就全局生效,且无法卸载;多个版本共存时,后注册的会覆盖先注册的——所以组件名要带业务前缀(如 acme-button),避免第三方库冲突。



















