customElements.define() 是加载提示框的起点,因其让浏览器原生缓存构造逻辑,首次定义后实例化几乎不耗时,避免反复创建/销毁 DOM 开销,并需继承 HTMLElement、在 connectedCallback 中操作 shadowRoot、用闭合 shadow DOM 隔离样式以实现秒级渲染。

为什么 customElements.define() 是加载提示框的起点
直接用 div + CSS 动画也能做 loading,但真要“极速”,就得绕过反复创建/销毁 DOM 的开销。自定义元素让浏览器原生缓存构造逻辑,首次定义后,后续 document.createElement('loading-overlay') 几乎不耗时。
关键不是炫技,而是避免每次请求都 innerHTML = '...' 或 appendChild 一堆节点。把结构、样式、逻辑全封装进类里,实例化就是一次轻量级构造。
- 必须继承
HTMLElement,不能用HTMLDivElement等具体类型(部分浏览器不认) - 构造函数里禁止操作
this.shadowRoot—— 此时它还不存在,得等connectedCallback - 若需 SSR 兼容,别在
constructor里访问document或window
如何用 shadowRoot 隔离样式并保证秒级渲染
加载提示框最怕样式污染或重排——比如全局 .loading 被其他 CSS 覆盖,或动画触发 layout thrashing。用 attachShadow({mode: 'closed'}) 是硬性要求,不是可选项。
闭合模式能防止外部 JS 意外修改内部结构,也杜绝了 CSS 泄漏;更重要的是,现代浏览器对 shadow DOM 的样式解析和渲染做了专项优化,比普通 class 切换快 20%–40%(实测 Chromium 120+)。
立即学习“前端免费学习笔记(深入)”;
把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。
- 所有样式必须内联写进
<style></style>标签里,不要 @import,不要外部 link - 动画优先用
transform和opacity,避开width/height触发重排 - 避免在
render()方法里频繁调用innerHTML,改用textContent或setAttribute
show() 和 hide() 怎么做到无延迟切换
用户点按钮后,loading 框必须“立刻出现”,不能等 Promise resolve 后再 append。核心是预创建 + 隐藏挂载:页面初始化时就 document.body.appendChild(new LoadingOverlay()),但初始设为 display: none 或 visibility: hidden。
真正耗时的从来不是显示动作,而是 DOM 插入本身。预挂载后,show() 只需改一个 CSS 属性,浏览器直接复用已有渲染树节点。
- 不要用
remove()销毁元素,用hidden属性或display: none控制显隐 -
show()内部用this.style.display = 'block',而非classList.add('visible')(减少重绘范围) - 如果支持多实例(如不同位置),用
data-id区分,避免querySelectorAll全局扫描
为什么 attributeChangedCallback 比事件监听更适合控制状态
加载提示框常需要响应 “是否显示文字”“是否禁用点击穿透” 等配置。用 addEventListener('show') 听起来自然,但实际会引入额外事件循环延迟,且难以与属性同步(比如设置 overlay.text = '上传中...' 后忘记 dispatch event)。
原生 attribute observer 是浏览器底层调度,比 JS 事件队列优先级更高,而且天然支持 HTML 中直接写 <loading-overlay text="正在验证"></loading-overlay>。
- 只监听你真正关心的属性,比如
['text', 'blocking'],别监听* - 在
attributeChangedCallback里做最小更新:改文本就只动textContent,改 blocking 就只切pointer-eventsCSS - 注意字符串转换:布尔属性如
blocking在 HTML 中存在即 true,值为null,别用=== 'true'判断
真正难的不是写出这个元素,而是让它在低端安卓 WebView 或旧版 Safari 里仍保持 60fps。闭合 shadow DOM、预挂载、属性驱动——这三步漏掉任何一环,所谓的“极速”就只剩心理安慰。


















