web components 规范要求:①命名须小写+短横线(如my-button);②constructor仅调用super()和attachshadow();③属性监听需声明observedattributes并响应attributechangedcallback;④内容分发依赖slot,样式须置于shadow dom内。

编写符合规范的 Web Components 自定义组件,核心是严格遵循浏览器原生标准,不是写得“能用”就行,而是要避开常见陷阱、保证可维护性和跨环境稳定性。下面从命名、结构、生命周期、内容分发四个关键点说清楚。
自定义元素名必须含短横线且全小写
这是硬性校验,不满足直接抛 DOMException。浏览器靠短横线(kebab-case)区分原生标签和自定义标签,防止未来冲突。
- ✅ 合法:
data-table、my-button、x-slider—— 全小写 + 至少一个短横线 + 不在首尾 - ❌ 非法:
mybutton(缺短横线)、MyButton(含大写)、-my-btn(短横线开头)、my-btn-(结尾)、123abc(纯数字字母) - 注册时必须用
customElements.define('my-button', MyButton),名字和类名不强制一致,但建议保持语义对应
constructor 中只做初始化,不操作 DOM 或属性
此时元素尚未挂载,this.shadowRoot 为空,this.getAttribute() 可能读不到初始值,强行操作易失败或被静默忽略。
- 必须调用
super(),否则继承链断裂 - 必须在此处调用
this.attachShadow({ mode: 'open' }),且只能一次;重复调用会报错 - 不要读
this.innerHTML、this.children或设置子节点;也不要在 constructor 里调用setAttribute - 如需初始化 shadow DOM 内容,留到
connectedCallback中处理
属性变更必须显式声明并响应
attributeChangedCallback 不是自动监听所有属性,它只对 observedAttributes 白名单里的 HTML 属性生效,且仅响应通过 setAttribute、removeAttribute 或模板中写死的变更。
- 在类中定义静态 getter:
static get observedAttributes() { return ['disabled', 'loading']; } - 属性名大小写必须完全一致,
'disabled'≠'Disabled' - JS 直接赋值(如
el.disabled = true)不会触发该回调,如需响应,应配合 setter 或改用其他机制 - 回调中可安全操作
this.shadowRoot,但注意:首次挂载时属性可能已存在,需兼容初始值
slot 是内容分发的唯一标准方式
没有 <slot></slot>,外部传入的子内容(比如 <my-card>Hello</my-card> 中的 Hello)就完全不可见,也不会渲染。
-
<slot></slot>支持name属性实现具名分发,如<slot name="header"></slot> - 默认内容写在
<slot></slot>标签内,当无对应内容插入时显示 - 若需监听 slot 内容变化(比如用户动态增删子节点),需手动监听
slotchange事件:shadowRoot.querySelector('slot').addEventListener('slotchange', ...) - 样式必须写在 shadow DOM 内(如
shadowRoot.innerHTML = '<style>...</style>...'),外部 CSS 默认无法穿透











