不是必须在 dom ready 后调用,但需确保类已声明且浏览器支持;应包裹在 'customelements' in window 判断中,推荐监听 domcontentloaded 或使用 defer/type="module" 脚本。

customElements.define() 必须在 DOM ready 之后调用吗?
不是必须,但绝大多数情况下你得等 DOMContentLoaded 或至少确保类已声明、浏览器支持可用。直接在 <script></script> 标签里调用 customElements.define() 而不加任何防护,会触发 TypeError: Failed to execute 'define' on 'CustomElementRegistry': this name has already been used' 或更常见的 ReferenceError: MyElement is not defined。
常见错误现象:页面白屏、控制台报错、自定义标签被当作普通 HTMLUnknownElement 渲染(无样式无行为)。
- 不要把
customElements.define()放在<script></script>开头就执行,除非你确认类已在前面声明且浏览器支持customElements - 推荐写法是包裹在
if ('customElements' in window)判断内,并监听DOMContentLoaded,或使用defer属性加载脚本 - 若用模块化(
type="module"),脚本默认延迟执行,此时可直接调用customElements.define(),无需额外等待
connectedCallback 里能操作 shadow DOM 吗?
能,而且这是最安全、最常规的操作位置。构造函数(constructor)里只能做基础初始化(如调用 super()、设置私有属性),不能访问 this.innerHTML、不能调用 this.attachShadow() —— 浏览器会抛出 NotSupportedError: Failed to execute 'attachShadow' on 'Element': Shadow root cannot be created on a custom element with existing children。
原因在于:元素在构造阶段尚未插入 DOM,也没有子节点权限;而 connectedCallback 触发时,元素已挂载,DOM 状态稳定,此时调用 this.attachShadow({ mode: 'open' }) 是标准做法。
-
constructor中只做轻量初始化:绑定this、声明私有字段、设置默认属性值 -
connectedCallback中创建 shadow DOM、渲染模板、添加事件监听器、读取属性(this.getAttribute('xxx')) - 如果需要响应属性变化,记得在类上声明静态
observedAttributes数组,并实现attributeChangedCallback
自定义内置元素(extends: 'button')为什么必须用 is 属性?
因为 HTML 解析器只认标准标签名。当你写 <button is="my-button"></button>,浏览器解析时仍按 HTMLButtonElement 实例化,再根据 is 查找注册的扩展类;而写 <my-button></my-button> 会被当成独立自定义元素(Autonomous Custom Element),哪怕你注册时指定了 { extends: 'button' },它也不会继承按钮的原生行为(如空格/回车触发 click、表单提交逻辑、可聚焦性)。
容易踩的坑:
- 误以为
customElements.define('my-button', MyButton, { extends: 'button' })后就能直接用<my-button></my-button>—— 实际无效,必须用is - 忘记给
is值加引号,写成<button is="my-button"></button>(HTML 属性值未引号包裹 → 解析失败) - 在 SSR 或静态 HTML 中硬编码
is,但服务端未预执行 JS 注册逻辑 → 元素无行为,仅保留原生 button 外观
shadow DOM 下的 CSS 选择器为何不继承外部样式?
这不是 bug,是设计目标:shadow DOM 的样式作用域天然隔离。外部定义的 p { color: blue; } 不会穿透进 shadow root,哪怕你在 shadow 内放了一个 <p></p> 标签。这是封装性的核心保障,但也意味着你得显式处理样式继承点。
实际影响:
- 无法靠全局 reset.css 统一控制 shadow 内文本颜色、字体大小等基础样式
-
:host和:host-context()是关键出口:前者匹配自定义元素自身(可用于设 display 或 font-size),后者可响应外部 class(如:host-context(.dark-theme) { color: white; }) - 若需复用外部 CSS 变量,必须在 shadow 内显式声明:比如
:host { --text-color: var(--text-color, #333); },否则变量不会自动透传
真正难搞的不是“怎么写样式”,而是“什么时候该让样式穿透、什么时候该守住边界”——比如一个 <icon-button></icon-button> 需要继承父容器的 font-size 来缩放 SVG,就得靠 CSS 自定义属性 + :host 主动接收,而不是指望继承链自动生效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











