::part()是唯一被标准支持且主流浏览器实现的shadow dom样式穿透机制,其生效前提是组件内部节点明确声明part属性,且仅对外部样式有效;::slotted()用于投射内容,二者不可混用。

::theme 并不存在——浏览器不支持 ::theme 伪元素,它不是标准 CSS 规范的一部分,也不是 Web Components 的任何草案或实现。所有声称可用 ::theme 穿透 Shadow DOM 的写法,要么是混淆了旧提案(已废弃),要么是误传。真正可用、有规范支撑、被主流浏览器实现的,只有 ::part()。
为什么 ::part() 不生效?常见失效原因
不是选择器写错了,而是根本没“暴露出口”:
- 组件内部的 HTML 节点没加
part="xxx"属性,::part(xxx)就完全匹配不到——浏览器连尝试匹配都不会做,静默跳过 -
part="btn"和::part(button)拼写/大小写不一致,直接失效(大小写敏感) - 在同一个 Shadow Root 内部使用
::part(),比如在组件自己的<style></style>里写my-button::part(base) { ... }——::part()只对外部(light DOM 或父级 shadow)有效,不能自用 - 目标节点被
<slot></slot>投射进来,但你用了::part()而不是::slotted();或者节点嵌套太深,::part()不支持链式选择(如::part(icon)::before非法)
::part() 和 ::slotted() 到底该用哪个?
看样式目标属于谁的 DOM:
- 想改组件自己 Shadow DOM 里**原生渲染的节点**(比如按钮内部的
<span part="label"></span>),用::part(label) - 想改被
<slot></slot>**投射进来的外部内容**(比如你在<my-button><svg slot="icon"></svg></my-button>里传入的 SVG),用::slotted([slot="icon"]) -
::slotted()无法选中投射内容内部的子元素(除非那个子元素也被<slot></slot>二次分发) -
::part()不能替代继承——color、font-family这类可继承属性天然穿透,不需要::part();但margin、background、display必须靠它或 CSS 自定义属性
兼容性与降级必须手动处理
Chrome 97+、Edge 97+ 原生支持;Safari 15.4+ 支持但需开启实验特性;Firefox 截至 2024 年中仍无计划支持。这意味着:
- 不能只写
my-comp::part(label) { color: red; },得用@supports selector(::part(foo))包裹 - 降级方案只能是 CSS 自定义属性:
@supports not selector(::part(foo)) { my-comp { --label-color: red; } },再由组件内部用var(--label-color)回收 - 别把关键视觉状态(如
disabled下的背景色)只交给::part()控制——一旦不支持,用户看到的就是未定义行为 - 真机测试比 CanIUse 更可靠,尤其在企业内网环境,Safari + macOS 12/13 组合仍大量存在
最易被忽略的一点:即使 ::part() 生效,它也只负责样式锚点暴露,不传递状态、不触发重绘逻辑、不替代 :host([disabled]) 这类条件匹配。组件作者若没在 observedAttributes 里声明对应属性,外部通过 :host([disabled]) ::part(x) 也无法响应属性变更。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











