::part() 仅匹配 shadow dom 内显式标注 part 属性的元素,大小写敏感、不可链式选择、不能自用,且完全依赖组件作者主动暴露;未加对应 part 属性则样式静默失效。

::part() 为什么写对了却没生效
因为 ::part() 不是穿透选择器,它只匹配 Shadow DOM 内部**显式带 part="xxx" 属性的元素**。外部写 my-input::part(label),但组件内部的 <span></span> 没加 part="label",样式就完全不应用——浏览器静默忽略,连控制台警告都没有。
- 大小写敏感:
part="Label"和::part(label)不匹配 - 拼写必须完全一致:
part="icon-slot"≠::part(icon) - 不能链式选择:
my-button::part(wrapper) input或my-button::part(label)::before是非法语法 - 不能在同一个 shadow tree 内自用:
::host::part(label)无效,::part()只能在 light DOM 或父级 shadow 中使用
组件作者必须主动暴露 part 属性
你作为调用方,没有任何办法绕过这一步。是否能用 ::part(),完全取决于组件是否在内部 HTML 节点上加了 part 属性。比如想定制加载图标颜色,组件里得这么写:
<div part="loader" class="spinner"></div>
否则你写 my-button::part(loader) 就是空转。没有第四个办法:!important、全局 class、把 <style></style> 放 全部无效。
- 常见暴露位置:slot 包裹层、状态指示器、图标容器、尺寸控制区
- 避免暴露语义模糊的 part 名,如
part="div1";推荐语义化命名:part="icon"、part="error-message" - 如果组件文档没列
part列表,直接查它的 shadow DOM 结构(DevTools → Elements → 展开 shadow root)看有没有part属性
::part() 和 ::slotted() 的分工边界
::slotted() 用于样式化被 <slot></slot> 投射进来的**外部内容**,而 ::part() 用于样式化组件**自己 shadow 内、带 part 属性的原生节点**。两者作用域和目标完全不同。
- 你想改
<my-button><span slot="icon">★</span></my-button>里 ★ 的颜色 → 用::slotted([slot="icon"]) - 你想改按钮内部那个旋转的
<div class="spinner"> → 组件得先加 <code>part="spinner",然后你写my-button::part(spinner) -
::slotted()能穿透一层 slot,但无法选中 slot 内部再嵌套的子元素(除非那层也用了<slot></slot>) - 兼容性:
::part()需 Chrome 97+ / Edge 97+ / Safari 16.4+;::slotted()支持更早(Chrome 53+)
配合 :host() 实现状态驱动样式
单靠 ::part() 只能设静态样式,真正灵活的定制要结合 :host() 的参数函数,比如根据属性切换样式:
:host([disabled]) ::part(button) { opacity: 0.5; cursor: not-allowed; }
但注意:只有在 static get observedAttributes() 里声明的属性变更,才会触发 :host() 重匹配。
- 推荐暴露常用状态属性:
disabled、size、variant,并在observedAttributes中列出 - 不要依赖 light DOM 的 class 触发
:host(.class)—— 宿主元素 class 变化不会触发 shadow 内样式更新 - 性能影响很小,浏览器只在对应属性变化时重新计算规则,不是实时监听所有属性
part,或者你误把 ::slotted() 的场景当成了 ::part() 的场景。











