自定义元素必须含且仅含一个短横线,命名仅限小写字母加短横线(如date-picker),禁止大写、下划线、数字开头或与原生标签同名;constructor仅调super()、attachShadow、初始化私有字段;DOM操作、属性读取、事件绑定须在connectedCallback中进行;需手动设置tabindex并绑定keydown事件;Shadow DOM样式需通过CSS自定义属性暴露定制入口。

自定义元素必须带短横线,否则注册直接失败
浏览器强制要求自定义标签名含且仅含一个短横线(-),my-button合法,mybutton、MyButton、my_button全都会抛出 DOMException: Failed to execute 'define' on 'CustomElementRegistry'。这不是兼容性问题,是规范硬限制。
常见错误现象:开发时本地跑通,上线后控制台一片红,但页面仍渲染出空白或降级为未知标签——大概率是标签名没加短横线,或用了保留名(如nav、header)。
- 合法命名:只用小写字母 + 短横线,如
date-picker、rich-text-editor - 禁止命名:含大写字母、下划线、数字开头、或与原生标签同名(
div、input、slot等) - 别试图绕过:哪怕用
document.createElement('mybutton')创建出来,也绝不会触发connectedCallback,行为不可控
constructor 里只能 super() 和初始化私有字段,DOM 操作必须等 connectedCallback
constructor 是唯一允许调用 super() 和创建 shadow root 的地方,但不能读属性、不能操作 DOM、不能发请求。很多同学一上来就写 this.innerHTML = '...' 或 this.getAttribute('value'),结果属性读不到、样式不生效、甚至报 NotSupportedError。
真正安全的时机是 connectedCallback:它在元素被插入 DOM 后触发,此时可放心读属性、渲染模板、绑定事件、设置初始 focus。
立即学习“前端免费学习笔记(深入)”;
-
constructor中只做三件事:调super()、this.attachShadow({ mode: 'open' })、初始化this._value = null这类私有字段 -
connectedCallback才负责:读this.getAttribute('value')、生成内部结构、添加事件监听器 - 若需响应后续属性变更,必须配合
static get observedAttributes()+attributeChangedCallback,且后者不处理初始值——首次渲染的属性得在connectedCallback里手动同步
键盘交互必须手动绑定,且 tabindex 不可省略
自定义元素默认不可聚焦、不响应 keydown,哪怕你把它渲染成按钮模样。浏览器只对原生可交互标签(button、input、带 tabindex 的元素)派发键盘事件。所以 <my-input></my-input> 默认按不动。
实操关键四步缺一不可:设 tabindex、监听 keydown、区分 event.code 与 event.key、精准 preventDefault()。
- 在
connectedCallback中设置this.setAttribute('tabindex', '0')(语义化场景可用'-1') - 用
this.addEventListener('keydown', this.handleKeydown.bind(this)),别用箭头函数,否则this指向丢失 - 快捷键逻辑(如 Ctrl+S)必须用
event.code === 'KeyS' && event.ctrlKey,event.key在不同键盘布局下不可靠 - 阻止默认行为前先判断上下文:
if (event.target === this && event.code === 'Enter') { event.preventDefault(); ... },避免干扰子元素原生行为
Shadow DOM 隔离样式是双刃剑,外部定制要靠 CSS 自定义属性
启用 Shadow DOM 后,外部 CSS 不会穿透进来,内部样式也不会漏出去——这本是封装优势,但团队协作中常因“样式全丢了”而放弃使用。根本原因不是 Shadow DOM 有问题,而是没显式注入样式或预留定制入口。
正确做法是:所有样式写进 shadowRoot,同时用 :host 暴露 2–3 个高频 CSS 自定义属性供外部覆盖,比如 --my-input-border-color、--my-input-font-size。
- 不要指望
<style>body { color: red}</style>影响 shadow 内容——shadow 中根本没有body - 在
connectedCallback中动态注入<style>:host { --my-input-bg: #f9f9f9 }</style>,或用shadowRoot.appendChild(styleEl) - 避免过度嵌套
:host(:not([disabled])) ::slotted(*)这类选择器,调试成本极高;子元素样式一律写在 shadow 内部,不依赖宿主穿透 - 产品提需求改颜色?只需在父容器加一句
my-input { --my-input-bg: #e6f7ff; },无需改源码、不发新包
CustomEvent)、插槽名(需注释说明),都得和团队对齐。否则别人用错属性、监听错事件、塞错 slot,调试起来比修祖传代码还累。



















