自定义元素命名必须含连字符且全小写,否则 customElements.define() 直接抛 DOMException;DOM 操作须在 connectedCallback 中执行;跨组件通信优先用带命名空间的 CustomEvent;表单参与需启用 formAssociated 并调用 attachInternals() 和 setFormValue()。

自定义元素命名必须含连字符且全小写
浏览器在调用 customElements.define() 时会立刻校验名称,不合规就直接抛 DOMException: The name "xxx" is not a valid custom element name。这不是警告,是硬性拦截——组件根本不会注册成功。
常见错误包括:mybutton(缺连字符)、MyButton(含大写)、user_profile(用下划线)、div(与原生标签重名)。唯一被接受的格式是像 date-picker、status-badge 这样全小写+ASCII短横线的组合。
命名不是风格偏好,而是规范强制项。语义要清晰(比如 form-fieldset 比 field-group 更明确),但不能牺牲可读性去缩写成 fld-grp。
DOM 操作必须放在 connectedCallback,别塞 constructor
constructor 只能做三件事:调用 super()、创建 Shadow DOM(this.attachShadow({ mode: 'open' }))、声明私有字段或绑定方法。任何涉及 this.shadowRoot、this.getAttribute()、querySelector 或事件监听的行为,都得挪到 connectedCallback。
立即学习“前端免费学习笔记(深入)”;
否则你会遇到这些现象:
-
this.shadowRoot是null,样式无法注入 - 首次渲染时属性读不到,因为元素还没挂载
-
fetch请求发出去了,但接收方还没注册监听器,结果丢数据
如果组件支持多次挂载(比如路由切换),记得在 disconnectedCallback 里清理定时器和事件监听器,否则内存泄漏。
跨组件通信优先用 CustomEvent + document 分发
不要用全局变量、单例对象或自定义事件总线。只要所有组件都在同一 document 下(即没跨 iframe),CustomEvent 就是最轻量、最标准的解耦方式。
关键实操点:
- 事件名加命名空间前缀,如
editor:run、console:log,避免冲突 -
detail必须是 plain object,不能是字符串、null或 DOM 节点;推荐结构:{ type: 'execute', payload: { code: '1+1', lang: 'js' } } - 分发用
document.dispatchEvent(new CustomEvent(...)),监听也统一用document.addEventListener('editor:run', ...) - 如果某组件在 Shadow DOM 内部,又想让外部监听,得设
bubbles: true;否则事件卡在 shadow boundary
注意:CustomEvent 不跨 iframe。主页面 dispatch 的事件,iframe 里收不到——得用 postMessage 中转,或直接调用 iframe.contentWindow.dispatchEvent()(同源前提下)。
表单参与能力必须显式启用 formAssociated
想让自定义元素真正参与表单提交(比如被 form.elements 捕获、响应 submit 事件),光渲染一个输入框远远不够。必须走标准路径:
- 定义时传
{ formAssociated: true }给customElements.define() - 在
constructor中调用this.attachInternals() - 在
connectedCallback中监听子<input>的变化,并调用this.internals.setFormValue()
别试图继承 HTMLInputElement——Chrome 100+ 已限制,Firefox/Safari 基本不支持。现实路径只有一条:自治型自定义元素(autonomous custom element),自己封装 UI,再通过 ElementInternals 接入表单生命周期。
最容易被忽略的是:formAssociated: true 缺失会导致 attachInternals() 报错;而 setFormValue() 不调用,表单 submit 时值就为空——调试时查不到源头,只看到后端收不到数据。



















