::slotted()不是穿透而是表面匹配,仅作用于slot分发的Light DOM直系子元素,必须写在shadow root内style中,不支持伪类/复合选择器,不匹配嵌套节点、文本节点及fallback内容。

为什么 ::slotted() 不是“穿透”,而是“表面匹配”
::slotted() 从不真正“穿透” Shadow DOM 边界,它只在 slot 分发完成后的视觉层做一次静态匹配——目标永远是 Light DOM 中**被分发进来的直系子元素**,不是 Shadow DOM 内部节点,也不管这些元素内部嵌套了什么。传入 <my-card><div><p>hi</p></div></my-card>,::slotted(div) 能生效,::slotted(p) 必然失效,因为 p 不在插槽“表面”。
::slotted() 必须写在哪?位置错就完全无效
它只能出现在 Shadow DOM 内部的 <style> 标签里,比如 shadowRoot.innerHTML = '<style>::slotted(button) { margin: 4px; }</style>'。全局 CSS、<link rel="stylesheet">、甚至宿主元素上的 style 属性,对它都毫无作用。浏览器会静默忽略,不报错也不生效。
- ✅ 正确:
shadowRoot.appendChild(document.createElement('style')).textContent = '::slotted(.icon) { opacity: 0.8; }'; - ❌ 错误:
document.querySelector('head').appendChild(...)里的同规则 - ❌ 错误:
<my-button style="--icon-opacity: 0.8">然后试图用::slotted(.icon) { opacity: var(--icon-opacity); }——变量不会继承到 ::slotted() 作用域
命名插槽和默认插槽怎么选?别靠猜
命名插槽必须显式用 [name="xxx"] 匹配,不能靠结构或顺序推断。默认插槽(无 name)推荐用 ::slotted(:not([name])),比 ::slotted(*) 更安全,避免误选带 name 属性但未被分发的节点。
-
<slot name="header"></slot>→ 对应::slotted([name="header"]) -
<slot></slot>→ 推荐::slotted(:not([name])),而非::slotted(*) - 纯文本节点(如
<my-card>hello</my-card>)不参与匹配,::slotted(span)永远不会命中
样式优先级和 fallback 的边界在哪?
::slotted() 的样式权重要低于 Light DOM 中同选择器的直接声明。你在外部写了 p { color: blue; },又在 shadow 里写了 ::slotted(p) { color: red; },最终显示的还是蓝色。它不是覆盖工具,而是轻量适配层。
立即学习“前端免费学习笔记(深入)”;
- fallback 文本(
<slot>暂无内容</slot>中的文字)完全不在::slotted()作用域内,无法控制 - 无法隐藏内容(
::slotted(.ad) { display: none; }属于业务逻辑误用) - Safari 16.4 前不支持属性选择器(如
[disabled])和伪类,复杂选择器慎用
::slotted() 就可能失效,且毫无提示。


















