customelements.define 必须在 html 解析前执行,否则未注册的自定义标签会变成无法升级的 htmlunknownelement;动态插入时需显式调用 customelements.upgrade;connectedcallback 会多次触发,须防重复初始化。

customElements.define 必须在 HTML 解析前执行
浏览器解析 HTML 时,遇到未注册的自定义标签(如 <data-table></data-table>)会立即创建 HTMLUnknownElement 实例,之后再调用 customElements.define('data-table', DataTable) 不会自动升级已有节点——这些节点永远卡在“未知”状态,connectedCallback 永不触发。
常见错误是把所有 customElements.define 放在 window.onload 或动态 import() 回调里,此时 DOM 已解析完毕,大量节点已“冻僵”。
- 正确做法:用内联
<script></script>(无defer/async),或type="module"+defer,确保注册早于 HTML 解析完成 - 若无法控制 HTML 输出顺序(如 CMS 渲染),必须手动升级:遍历匹配节点并调用
customElements.upgrade(node) - 注意:只对新插入的节点升级,不要对整个容器反复调用,避免重复 work
动态插入含自定义标签的 HTML 后必须显式 upgrade
通过 innerHTML、insertAdjacentHTML 或框架的 v-html/dangerouslySetInnerHTML 插入含自定义标签的字符串时,浏览器不会识别新节点,它们仍是 HTMLUnknownElement,生命周期完全静默。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
必须在插入后立即调用 customElements.upgrade(el),且该调用只作用于 el 及其子树。
- 不要对父容器反复调用
upgrade,尤其在列表渲染中易引发性能抖动 - 大量节点时,用
queueMicrotask或requestIdleCallback分片执行,防主线程阻塞 - 更优方案:结合
MutationObserver监听新增节点,只对新增部分升级,避免全量扫描
connectedCallback 中必须防重复初始化
connectedCallback 在元素每次被插入 active DOM 树时都会触发,包括 Vue/React 列表重渲染、display: none 切回、appendChild 多次调用等场景。把它当“构造函数”用,会导致 Shadow DOM 多次 attach、事件重复绑定、定时器叠加、fetch 请求发多次。
- 典型做法:用私有属性标记是否已初始化,如
this._initialized = true,首次进入时设置,后续直接 return - 避免在
connectedCallback中做 heavy work(如解析大 JSON、创建复杂 Canvas),应延迟到requestIdleCallback或按需触发 - 若依赖外部数据,建议用
Promise缓存加载结果,避免重复请求
大规模注册时别忽略同步升级开销
customElements.define 本身不阻塞解析,但若批量调用且未控制时机(比如在模块顶层一口气 define 200 个组件),会引发大量同步升级开销——真正卡顿来自挂载阶段的重复初始化和 DOM 遍历,不是注册本身。
- 非首屏组件可延迟注册:用
IntersectionObserver或路由懒加载触发define - 注册后立即使用?确认是否真需要——很多组件其实只在特定交互路径中才出现
- 注意:自定义内置元素(如
customElements.define('my-button', MyButton, {extends: 'button'}))注册后仍需在 HTML 中写<button is="my-button"></button>,否则无效
connectedCallback 的触发频率远超预期,且它不像 constructor 那样只跑一次**。哪怕只是切换 tab 或折叠展开,都可能反复进入。防重复初始化不是“锦上添花”,而是性能底线。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










