::part()不是自动穿透通道,而是需组件作者在shadowroot内用:host ::part(...)显式声明并透传的样式锚点,仅作用于带part属性的节点且大小写敏感,不支持链式选择或外部直接定义。

part属性不是Shadow DOM样式穿透通道
HTML的part属性本身不提供跨Shadow DOM边界的样式能力,它只是为外部CSS提供一个“命名锚点”,但能否生效完全取决于宿主元素是否启用:host或::slotted()显式暴露该part——浏览器不会自动把[part="header"]变成可被外部选择的目标。
常见错误现象:<my-card><div part="header">Title</div></my-card>,然后在全局写div::part(header) { color: blue; },结果无效。原因不是语法错,而是my-card没在shadowRoot里用:host ::part(header)做转发。
-
part必须配合::part()伪元素使用,且仅在宿主元素的shadowRoot内定义了对应part值时才可被外部定位 -
::part()只能作用于light DOM中带part属性的节点(即<slot></slot>分发进来的内容),对shadow内部原生节点无效 - Chrome 95+、Firefox 96+、Safari 17.4+支持
::part();Edge 95+同步;旧版本直接忽略规则 - 不能嵌套写
::part(header) .title——::part()是原子选择器,后面跟的子选择器会被浏览器丢弃
如何让part真正生效:必须由组件主动透传
组件作者必须在shadowRoot内用:host ::part(...)显式声明哪些part允许被定制,否则外部::part()选择器永远匹配不到任何节点。
正确做法示例:
const shadow = element.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<slot name="header"></slot><slot name="body"></slot>
`;
const style = document.createElement('style');
style.textContent = `
:host ::part(header) { color: var(--header-color, #1e40af); }
:host ::part(body) { padding: 1rem; }
`;
shadow.appendChild(style);
- 只写
::part(header)不加:host前缀?无效——::part()必须出现在shadowRoot的样式表里,且需绑定到宿主上下文 - 想让
part="footer"也生效?必须在style.textContent里补上对应规则,不能靠外部CSS“猜” -
part值区分大小写,part="Header"和part="header"是两个不同锚点 - 多个part可共用一个规则:
:host ::part(header), :host ::part(footer) { border-top: 1px solid #e5e7eb; }
part vs ::slotted():别混用,职责分明
::slotted()处理的是传入<slot></slot>的顶层节点样式,::part()处理的是这些节点上显式标注的命名区域——它们解决的是不同层级的定制需求,不能互相替代。
典型误用:
- 给shadow内部自己写的
<div class="inner-header">加<code>part="header"——无效,part只对light DOM节点有效 - 写
::slotted([part="header"]) { ... }——::slotted()不支持属性选择器,只认元素名或* - 用
part代替:host控制容器尺寸——part无法影响布局属性(display、width、margin等),这些必须走:host或CSS变量 - 期望
::part()能继承父级CSS变量——它能,但变量必须挂载在宿主元素上,比如<my-card style="--header-color: red;"></my-card> - Vite/webpack打包时若启用了CSS提取(如
mini-css-extract-plugin),style.textContent里的::part()可能被剥离到全局CSS文件,导致规则脱离shadowRoot上下文而失效 - Safari 17.0–17.3对
::part()支持不完整,part值含连字符(如part="main-content")会解析失败,建议用驼峰(mainContent)或下划线 - 部分UI库(如Lit、Stencil)的模板引擎默认不转义
part属性,但若手动拼接字符串注入shadowRoot,需确保part值被正确序列化,避免XSS或属性丢失 - DevTools里能看到
part属性,但::part()规则未生效?先检查shadowRoot里是否有:host ::part(...)声明——没有就等于没开门
真实项目中最容易被忽略的兼容性细节
哪怕写了正确的:host ::part(),上线后仍可能失效——问题往往出在构建工具链或运行时环境上。
part不是“开放访问”,而是“有限授权”。每一处::part()都得亲手写出来,漏一条,外部就定制不了那一块。











