shadow dom本身不优化性能,需显式注入样式、优先用外部css、设置contain: strict、用css变量传主题、慎用::slotted()。

Shadow DOM 本身不自动优化性能,关键在于怎么构造和管理样式表——它只提供隔离能力,真正减少开销靠的是控制样式作用域、避免无效匹配、合理复用资源。
显式注入样式,别依赖外部CSS
外部样式表(比如全局 <link> 或父组件的 <style></style>)默认不会进入 Shadow DOM 的样式计算流程。想让样式生效,必须把 <style></style> 或 <link rel="stylesheet"> 显式 append 到 shadowRoot 中。
- ✅ 正确:在
connectedCallback里创建<link>并挂载到 shadowRoot - ❌ 错误:把样式写在页面
里,指望它“穿透”进 shadow;或用innerHTML = '<style>...'</style>,这种内联写法在某些场景下无法参与 CSSOM 构建 - ⚠️ 注意:
mode: 'closed'下 JS 无法访问 shadowRoot,连注入样式都做不到,开发阶段建议用open
优先用外部样式表,而非内联
虽然可以往 shadowRoot 里塞 <style></style>,但上线时更推荐加载外部 CSS 文件——它能被浏览器缓存,多个组件实例共享同一份样式,减少重复解析和内存占用。
- 用
import.meta.url定位相对路径,避免硬编码 URL - 配合构建工具生成 contenthash,实现精准缓存更新
- 不要在每次实例化时都动态
fetch同一份 CSS,可提前预加载或复用已加载的 sheet
加 contain: strict,激活渲染边界
仅 attachShadow 不够,没加 contain: strict,浏览器仍可能为布局和绘制做全局依赖检查,隔离就只是视觉上的。
- 必须在 shadow 内部的
<style></style>中,给:host或根容器设置contain: strict - Safari 16.4+ 和 Chrome 73+ 支持良好;旧版可用
contain: layout paint降级 - 别在 JS 创建节点后再补样式——样式节点必须和 DOM 同步挂载,否则 containment 不生效
用 CSS 自定义属性传主题,别写穿透选择器
想让宿主的主题色透入 shadow,别用 :host-context(.dark) .btn 这类高权重、仍需匹配的选择器——它增加计算量,还耦合 class 名。
- 宿主元素直接设
style="--text-color: #1e1e1e" - shadow 内写
.label { color: var(--text-color, #333); } - 变量可从宿主或其祖先读取,
document.documentElement上的:root变量也能访问 - 禁止用
!important或已废弃的/deep/——Chrome 120+ 和 Firefox 115+ 已移除支持
慎用 ::slotted(*) 和动态 innerHTML
::slotted(*) 看似方便,但它会触发对所有分发节点的样式重计算;频繁赋值 shadowRoot.innerHTML 会导致节点重建、事件丢失、全量重排。
- 避免写
::slotted(*) { margin: 1rem; }——margin不可继承,这条规则完全无效 - slot 内容变更不会自动重绘,需监听
slotchange并节流处理 - 批量更新优先用
DocumentFragment,或选用 Lit、Hybrids 等带 diff 能力的库











