自定义元素需手动控制 define 和实例化时机,否则拖慢首屏;define 可晚于 HTML 解析但须早于首次访问节点,非首屏组件应推迟到交互前 define,首屏组件宜用 defer 模块同步 define,且必须配合 upgrade 激活已存在节点。

自定义元素(Custom Elements)本身不懒,必须手动控制 define 和实例化时机,否则反而拖慢首屏。
customElements.define() 该在什么时候调用
浏览器解析到 标签时,若尚未 define,会先以“unknown”元素挂载 DOM;等 define 后才升级。这意味着 define 可以晚于 HTML 解析,但必须早于首次访问该节点(比如 document.querySelector('my-card') 或 DOMContentLoaded 后的首个 microtask)。
- 非首屏组件(如弹窗、设置页模块):推迟到用户交互前再 define,例如
button.addEventListener('click', () => { customElements.define('my-dialog', MyDialog); }); - 首屏可能用到的组件:放在
<script type="module" defer></script>中同步 define,确保脚本不阻塞 HTML 解析 - 绝对不要在全局脚本顶部批量 define 所有组件——尤其当其中依赖未就绪或含大量模板/样式时,会浪费 CPU 和内存
define 之后组件不渲染?检查升级时机
define 只是注册构造函数,不等于实例化。组件是否激活,取决于它是否被插入已连接的 DOM 树中。
- 组件被
display: none或hidden包裹,但仍在 connected DOM 中 → 会 upgrade,但不可见 -
document.createElement('my-card')后未 append 到 document → 不 upgrade,connectedCallback不触发 - 组件在 Shadow DOM 内部创建,但宿主元素尚未连入文档 → upgrade 被延迟,直到宿主连入
- 用了
customElements.whenDefined('my-card').then()却没处理 reject(如组件名拼错、重复 define 报错DOMException: "Failed to execute 'define'")
真正按需加载:动态 import + 条件 define
仅延迟 define 不够,JS 模块本身也得延迟下载和执行。现代方案是结合 import() 动态导入 + 条件注册:
- 正确写法:
button.addEventListener('click', async () => { const { default: MyCard } = await import('./my-card.js'); customElements.define('my-card', MyCard); }); - 预加载优化:hover 后 200ms 内大概率点击?可用
import('./my-card.js').catch(() => {})提前触发 fetch(不 await,不阻塞) - 路径必须是静态字符串(如
import('./chart.js')),不能拼接变量,否则 Webpack/Vite 无法生成独立 chunk - 同一名称重复 define 会抛错,务必确保模块只被 import 一次,或加 guard 判断:
if (!customElements.get('my-card')) { ... }
已有节点怎么激活?upgrade() 很关键
如果组件标签已在 HTML 中(SSR 或静态写死),但 define 发生在 DOM 加载后,这些节点不会自动 upgrade —— 它们仍是 “unknown” 元素。
- 必须显式调用
customElements.upgrade(node)激活已存在的节点 - 常见模式:
document.querySelectorAll('x-chart').forEach(el => customElements.upgrade(el)); -
customElements.whenDefined('x-chart').then(() => { ... })只保证定义就绪,不自动 upgrade 已存在节点 - upgrade 只对当前已挂载的节点生效;新插入的节点会自动 upgrade(前提是已 define)
最容易被忽略的是:define 和 upgrade 是两个独立动作,且 upgrade 必须在 define 之后、且针对已挂载节点。很多“组件不渲染”的问题,其实卡在 upgrade 这一步,而不是 define 没执行。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











