Shadow DOM 是浏览器原生的 DOM 隔离机制,可在元素内创建物理隔离的独立子树,实现样式和脚本的作用域边界;需用 attachShadow({mode}) 创建,支持 open/closed 模式;样式通过 :host、::slotted 等伪类有限穿透,事件默认不越界,slot 内容保持外部样式控制。

Shadow DOM 是什么:浏览器原生的 DOM 隔离机制
Shadow DOM 不是框架发明的概念,而是浏览器提供的底层能力,用来在单个元素内部创建一个独立的 DOM 子树——这个子树和外部文档的 DOM 是物理隔离的。它不等于 Web Components,但它是 Web Components 的核心支柱之一。
关键点在于“隔离”:外部 CSS 选不到 shadow 内部的节点,document.querySelector 找不到它,event.target 穿透时默认也不会暴露内部结构(除非显式配置 composed: true)。
如何创建 Shadow Root:attachShadow vs createShadowRoot
createShadowRoot() 已废弃多年,必须用 element.attachShadow()。它接受一个 { mode: 'open' | 'closed' } 参数,决定是否允许 JS 从外部访问 shadow root。
-
mode: 'open':可通过el.shadowRoot直接读取,调试工具里也能展开查看 -
mode: 'closed':el.shadowRoot返回null,即使你持有该 shadow root 的引用,也无法通过元素反查
const host = document.createElement('x-button');
const shadow = host.attachShadow({ mode: 'open' });
shadow.innerHTML = ``;
样式封装怎么生效:scoped CSS 的真正原理
Shadow DOM 的样式隔离不是靠“自动加前缀”,而是靠**作用域边界**:CSS 选择器在 shadow root 内部解析时,只匹配其内部的节点;外部样式表中的规则,若未使用 :host、::slotted 等特殊伪类,根本不会进入 shadow 内部计算。
立即学习“Java免费学习笔记(深入)”;
常见误区:
- 以为
标签写在 shadow 里就“自动封装”——其实只是因为它被插入到 shadow root 中,自然受限于作用域 - 试图用
.my-class从外部控制 shadow 内部按钮颜色——无效,除非用:host > .my-class或开放part属性配合::part() - 忘记
:host只能写在 shadow 内部的样式中,用来匹配宿主元素自身
哪些地方容易出问题:跨边界通信与样式穿透
Shadow DOM 隔离很干净,但实际开发中常需要“有限穿透”:
- 想让外部传入的 CSS 变量影响 shadow 内部?可以,但需在 shadow 内显式使用
var(--color),且外部需在宿主元素上声明该变量 - 想让外部样式覆盖 shadow 内部默认样式?不能直接写
x-button button { ... },得用:host-context(.theme-dark) button { ... }或开放exportparts - 事件监听不到 shadow 内部点击?因为事件默认不冒泡出 shadow boundary;要用
composed: true触发,或监听在host上并启用slotchange/click等可穿出事件
最易忽略的是:slot 元素的内容虽然渲染在 shadow 内,但它仍是外部 DOM 节点,样式仍受外部控制——这是有意为之的设计,不是 bug。











