customelements.define() 必须在 dom ready 后执行,且避免重复注册;constructor 中仅可 super() 和 attachshadow();attributechangedcallback 需匹配 observedattributes 白名单;内容分发依赖 ,样式须置于 shadow dom 内。

customElements.define() 必须在 DOM ready 后执行
直接把 customElements.define() 写在 <script></script> 标签里、且没加任何时机控制,组件会注册失败——浏览器找不到类定义或报 Failed to execute 'define' on 'CustomElementRegistry': this constructor is not a constructor。
常见错误写法是把类定义和注册写在 里,此时 JS 执行时 DOM 还没加载,class MyButton extends HTMLElement 虽然能声明,但若依赖了尚未解析的全局变量或模块,就可能出错;更隐蔽的问题是:某些构建工具(如 Vite 的 dev server)会动态注入脚本,导致执行顺序不可靠。
- 最稳妥做法:用
document.addEventListener('DOMContentLoaded', () => { ... })包裹注册逻辑 - 如果组件类定义在模块中(ESM),确保
import已完成再调用define,不要把define放在export外部顶层 - 避免在多个地方重复调用
customElements.define('my-button', MyButton),第二次会抛Failed to execute 'define' on 'CustomElementRegistry': the name "my-button" has already been used
constructor 中只能 super() + attachShadow(),别读属性或操作子节点
很多人在 constructor 里写 this.dataset.xxx 或 this.innerHTML,结果读到空值或 null——因为此时元素还没插入文档,HTML 属性虽已解析(dataset 可读),但子节点树尚未建立,this.children、this.querySelector 都不可用。
constructor 的唯一职责是初始化原型链和影子根,其他一切延迟到 connectedCallback。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
-
super()必须第一行,漏掉会导致实例无法继承HTMLElement方法(如getAttribute失效) -
this.attachShadow({ mode: 'open' })只能调用一次,且必须在constructor;在connectedCallback里再调会报This element already has a shadow root - 想读初始 HTML 属性(如
<my-input value="hello"></my-input>),改用attributeChangedCallback,或等connectedCallback触发后再用this.getAttribute('value')
observedAttributes 声明不匹配,attributeChangedCallback 就不会触发
写了 attributeChangedCallback 却发现属性改了没反应?90% 是因为 static get observedAttributes() 返回的数组和实际属性名对不上——它不是监听所有属性,而是白名单机制,且大小写敏感、不支持驼峰自动转换。
例如你希望响应 data-theme,但写成 return ['theme'] 或 return ['Data-Theme'],回调永远不会进。
- 必须返回字符串数组,如
return ['disabled', 'size', 'data-theme'] - 属性名必须和 HTML 中完全一致:
data-theme→'data-theme',不能写成'dataTheme'或'theme' - JS 直接赋值
el.disabled = true不触发该回调,只有el.setAttribute('disabled', '')、el.removeAttribute('disabled')或标签内写死(<my-btn disabled></my-btn>)才生效 - 首次挂载时,写死在标签上的属性会触发一次
attributeChangedCallback,但仅当该属性在observedAttributes列表中
slot 是内容分发的开关,没它外部内容就不可见
写了 <my-card>Hello</my-card>,但页面上啥也不显示?大概率是 shadow DOM 里没放 <slot></slot>。Shadow DOM 默认隔离内容,外部传入的文本、子元素不会自动透传进去。
<slot></slot> 不是可选功能,是内容分发的必需入口。它像一个“洞”,让外部内容流进来。
- 最简写法:
shadow.innerHTML = '<div class="card"><slot></slot></div>' - 支持具名插槽:
<slot name="header"></slot>对应<my-card><span slot="header">Title</span></my-card> - 样式必须写在 shadow DOM 内(
shadow.innerHTML += '<style>.card{...}</style>'),主文档的 CSS 无法穿透 - 别在
constructor里尝试用this.innerHTML获取内容——此时子节点未就绪;应在connectedCallback或slotchange事件中处理
<slot></slot>,没有它,组件就是个黑盒。**前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










