::part() 是组件作者主动暴露的受控样式接口,仅对 Shadow DOM 内显式声明 part 属性的元素生效,区分大小写,不作用于 slot 投影内容或宿主自身,且需注意浏览器兼容性(Safari 15.4+)。

part 属性不是样式穿透的“后门”,而是组件作者主动暴露的、受控的样式接口。它只在组件内部显式声明了 part 值的元素上生效,外部无法靠猜测或暴力选择器绕过——没设 part="xxx",::part(xxx) 就完全不匹配。
为什么 ::part() 选不到元素?常见原因全列在这里
最常遇到的现象是写了 my-component::part(label) { color: red; } 却毫无反应。这不是浏览器 bug,而是几个硬性前提没满足:
- 组件内部的对应元素必须真实存在
part="label"属性(注意:不是class="label",也不是自定义属性如data-part="label") -
part值区分大小写,part="Label"和::part(label)不匹配 - 该元素必须在 Shadow DOM 内部;如果它被
<slot></slot>投影进来(即来自 light DOM),::part()对它无效 - 宿主元素本身不能带
part属性——::part()只作用于 shadow 内部子节点,不作用于宿主自身
part 和 :host、::slotted() 的分工边界
三者解决的是不同层级的问题,混用或误替会导致样式失效:
-
:host:只控制宿主元素自身(比如<my-button></my-button>这个标签),可用于响应它的class或disabled属性,但改不了它内部任何子元素 -
::slotted():只影响通过<slot></slot>插入的内容(即使用者传进来的 HTML),且仅支持有限样式(如color、font-size),不能设display或margin -
::part():专为 shadow 内部原生节点设计,由组件作者决定哪些节点可被定制,支持完整 CSS 属性(包括border-radius、background、cursor等)
例如想把组件内一个按钮的光标改成 default,:host button { cursor: default; } 无效(外部样式穿不透),::slotted(button) 也无效(按钮不是插槽内容),唯一合法路径就是组件内部给它加 part="action-btn",再写 my-component::part(action-btn) { cursor: default; }。
立即学习“前端免费学习笔记(深入)”;
性能与兼容性:别在低版本 Safari 上赌 ::part()
::part() 是较新的 CSS 规范,在 Safari 中直到 15.4 才开始支持(2022 年 3 月),iOS Safari 15.4+ 同步支持。Chrome 97+、Firefox 96+ 已稳定支持。这意味着:
- 若需兼容 iOS 15.3 及更早设备,
::part()必须降级为 JS 方案(如通过shadowRoot.querySelector('[part="xxx"]')动态设 style) - 每个
::part()选择器都会触发一次 shadow 内部的样式匹配,大量使用(如 50+ 个不同part)可能轻微拖慢样式计算,但远不如滥用::slotted(*)或嵌套:host-context()影响大 - 不要用
part替代语义化结构——比如给一个图标元素设part="icon"合理,但给整个卡片容器设part="card"再试图用它覆盖所有子样式,就违背了封装本意,也容易引发维护混乱
真正关键的一点是:part 不是组件作者的负担,而是契约。它要求你在封装时就想清楚“哪些视觉细节我愿意交给使用者控制”,而不是留一堆 class 让人靠猜去覆盖。



















