customelements.define 必须在 dom 加载前注册以确保已存在元素被正确升级;attributechangedcallback 需显式声明 observedattributes 才能监听属性变更;shadow dom 内部节点必须通过 this.shadowroot 访问,且自定义元素名须含短横线并避开保留字。

customElements.define 必须在 DOM 加载后执行
如果脚本在 之前运行,而自定义元素标签已写在 HTML 中,浏览器会把 <my-button></my-button> 当作未知元素解析,后续即使调用 customElements.define() 也不会自动升级——它只会保持为“自定义未定义元素”,不触发 constructor 或 connectedCallback。
常见错误现象:页面渲染出空白或原始标签名,控制台无报错,但元素没行为、没 Shadow DOM。
- 确保脚本加
defer属性(放在内)或置于前 - 不要依赖
document.addEventListener('DOMContentLoaded', ...)后再注册——customElements.define()本身是同步的,但“升级”发生在元素插入 DOM 时;提前注册才能捕获已存在的元素 - 若动态创建元素(如
document.createElement('my-input')),注册顺序无关紧要,但必须在appendChild前完成
attributeChangedCallback 只监听白名单属性
默认情况下,attributeChangedCallback 不会响应任意属性变更。你必须在 customElements.define() 之前,显式声明哪些属性需要被观察,否则改了 value 或 disabled 都不会触发回调。
使用场景:你想让 <date-picker value="2026-07-01"></date-picker> 在属性变化时同步更新内部输入框;或让 size 属性控制按钮尺寸。
- 在类定义中添加静态 getter:
static get observedAttributes() { return ['value', 'size', 'disabled']; } - 属性名必须全小写(HTML 属性不区分大小写,但 JS 中统一转为小写)
- 只监听字符串值变更;布尔属性如
disabled出现即为true,移除即为null,oldValue和newValue会是null和""(不是布尔值)
attachShadow({ mode: 'open' }) 后不能再用 querySelector 搜子元素
Shadow DOM 是隔离的,外部 JS 无法直接访问其内部节点。如果你写了 this.shadowRoot.querySelector('button') 没问题,但若在组件外写 document.querySelector('my-counter').querySelector('button'),结果一定是 null——这是设计使然,不是 bug。
容易踩的坑:调试时习惯性用 DevTools 的 Elements 面板右键“Copy selector”,复制出来的是 my-counter button 这类选择器,实际无效;或者试图用 jQuery 绑定事件到 shadow 内部元素,发现事件不触发。
- 所有 DOM 操作必须限定在
this.shadowRoot下进行 - 需要对外暴露方法(如
focus()、reset())时,应在类上定义公开方法,而不是开放内部节点引用 - 想让外部 CSS 影响 shadow 内样式?用
:host、:host-context()或::slotted(),别尝试穿透
自定义元素不能省略连字符,且不能用保留字
浏览器强制要求自定义元素名必须包含至少一个短横线(-),比如 my-card 合法,myCard 或 card 直接抛 DOMException: Failed to execute 'define' on 'CustomElementRegistry'。
更隐蔽的问题是用了 HTML 保留标签名,例如 custom-elements 看似合规,但 elements 是全局对象,某些浏览器会拒绝注册;header、footer、menu 等内置标签名也禁止复用。
- 推荐命名模式:
project-name-component(如acme-modal)或domain-component(如ui-button) - 避免单字前缀(如
x-button),虽然技术可行,但语义弱且易与历史方案混淆 - 注册失败时,
customElements.define()不抛异常,但控制台会输出警告,务必打开 DevTools 查看
class MyBadge extends HTMLElement,而是让这个元素在不同上下文里稳定工作:被 SSR 渲染、被框架包裹、被多次重复挂载、在 iframe 里加载、甚至被其他自定义元素嵌套——这些边界情况,几乎全由注册时机、属性监听粒度和 Shadow DOM 边界意识决定。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











