自定义元素必须注册、命名合规、生命周期正确:标签名须含短横线且全小写;attachShadow()后DOM操作需在connectedCallback中执行;attributeChangedCallback仅响应observedAttributes白名单属性;表单控件需设formAssociated:true并用attachInternals()接入表单协议。

不能只写个 class 就当组件用——自定义元素必须注册、命名合规、生命周期对路,否则标签就是个空壳。
customElements.define() 报错 “The name is not a valid custom element name”
这是最常卡住的第一步:浏览器强制校验标签名格式,不合规就立刻抛错,根本不会执行后续逻辑。
- 必须含至少一个 ASCII 短横线(
-),比如my-button、data-input-field - 必须全小写,
MyButton、myButton都非法 - 不能以短横线开头或结尾,
-my-btn和my-btn-都不行 - 不能与原生标签同名,
div、slot、html全部被拦截
验证方式很简单:customElements.define('xxx', Class) 执行前,先手动检查字符串是否满足这三条;开发时建议加守卫:if (!customElements.get('my-button')) { customElements.define('my-button', MyButton); },避免重复注册报错。
constructor 里调用 attachShadow() 后,this.shadowRoot.innerHTML 不生效
这是因为 attachShadow() 虽然成功创建了影子根节点,但此时元素尚未挂载到文档中,shadowRoot 虽存在却不可靠——尤其在 SSR 或框架嵌套场景下,直接写入会被静默丢弃。
立即学习“前端免费学习笔记(深入)”;
-
attachShadow({ mode: 'open' })必须在constructor中调用,且只能一次 - 所有 DOM 操作(如
innerHTML、appendChild)必须推迟到connectedCallback中执行 - 不要在
constructor里读this.getAttribute('xxx')或this.innerHTML,属性还没解析完
正确做法:constructor 只做 super() + attachShadow() + 初始化私有字段;渲染和属性读取统一挪到 connectedCallback。
attributeChangedCallback 死活不触发?
它不是“监听所有属性变更”,而是严格按 static get observedAttributes() 白名单响应,且只对 HTML 属性操作有效(setAttribute、removeAttribute、标签内写死),JS 直接赋值(如 el.disabled = true)完全不触发。
- 确认
observedAttributes返回的是字符串数组,比如return ['disabled', 'loading'],不是对象或单个字符串 - 属性名大小写必须完全一致:
'disabled'≠'Disabled' - 初始属性值(如
<my-input disabled></my-input>)会触发一次回调,但oldValue是null,别直接用===判断 - 想同步 JS 属性赋值,得自己补逻辑:比如监听内部
input的change事件,再手动调用this.setAttribute('value', ...)
表单控件提交不了值?漏了 formAssociated: true 和 attachInternals()
很多自定义输入框渲染正常、样式也对,但点 <form> 提交时后端收不到值——因为没接入表单生命周期。这不是样式或事件问题,是协议层缺失。
- 必须在
constructor中调用this.attachInternals(),获取ElementInternals实例 - 注册时必须传
{ formAssociated: true }选项,否则attachInternals()会报错 - 在
connectedCallback中监听子<input>的input或change事件,并调用this.internals.setFormValue(...) - 不要尝试继承
HTMLInputElement,Chrome v100+ 已限制,Firefox/Safari 基本不支持——走自治型路线(autonomous custom element)是唯一稳定路径
最关键的细节:setFormValue() 接收的是原始值(字符串、数字、FileList 等),不是 DOM 节点;如果要支持多选或文件上传,得自己序列化并传入对应类型。



















