slot属性仅在shadow dom中生效,需父元素为注册的自定义元素且已attachshadow;::slotted()仅匹配直系子节点、不支持复杂选择器;slot内容不受样式隔离保护,须用::slotted()显式控制。

slot 属性不是全局属性,只在 Shadow DOM 中生效
很多人以为 slot 是像 id 或 class 那样的全局 HTML 属性,可以随便加在任意元素上——其实完全不是。slot 属性只有在 Web Components 的 Shadow DOM 环境中才被浏览器识别和处理,且必须满足两个硬性前提:
- 父元素是通过
customElements.define()注册的合法自定义元素(如<my-dialog></my-dialog>) - 该自定义元素内部已调用
this.attachShadow({ mode: 'open' })创建了 Shadow Root
写错一步,比如直接在普通 <div> 里写 <code><p slot="header">标题</p>,浏览器会把它当做一个无意义的自定义属性丢弃,不报错、不警告、也不分发。
::slotted() 样式只能穿透一层,且不支持复杂选择器
::slotted() 是唯一能从 Shadow DOM 内部影响 Light DOM 内容样式的伪元素,但它有非常严格的限制:
- 只匹配被分发进来的**直系子节点**,不能写
::slotted(div h2)或::slotted(.header > h2) - 只支持简单选择器:
::slotted(h2)、::slotted([slot="footer"])可用;::slotted(footer h2)无效 - 样式不会继承到嵌套更深的节点:如果外部传入的是
<article><h2>标题</h2></article>,::slotted(h2)仍生效,但::slotted(article h2)不行 - 它不影响事件委托或 JS 查询:用
shadowRoot.querySelector('::slotted(h2)')会返回null,因为::slotted()不是真实 DOM 节点
slot 内容默认不受 Shadow DOM 样式隔离保护
这是最容易被误判的一点:Light DOM 中的内容(即带 slot 属性的那些节点)本身仍在全局作用域,所以:
- 外部全局 CSS(如
p { color: red; })依然会命中 slot 分发进来的内容 - Shadow DOM 内部的
<style></style>默认对 slot 内容无效,除非显式用::slotted()或 CSS 自定义属性透传 - 想真正隔离样式,必须靠
::slotted()显式控制,或者把内容包装成另一个自定义元素(启用它自己的 Shadow DOM) -
!important在::slotted()规则里有效,但在外部全局规则里也有效——它不打破隔离,只是提高优先级
动态修改 slot 属性值必须用 setAttribute,不能靠模板绑定
原生 Web Components 不解析运行时表达式。以下写法全部无效:
-
<h2 :slot="currentSlot">标题</h2>(Vue 模板语法) -
<h2 slot="{dynamicName}">标题</h2>(JSX / 字符串插值) -
<h2 slot="{{ name }}">标题</h2>(模板引擎写法)
正确做法只有一种:
const el = this.shadowRoot.querySelector('h2');
el.setAttribute('slot', 'new-name');
注意:改完后要等下一个 microtask 才会触发 slotchange 事件,assignedNodes() 也需重新调用才能看到更新结果。别在 setAttribute 后立刻查,容易误判为“没生效”。











