shadow dom 是唯一能真正切断样式和 dom 查询泄漏的原生机制,必须在 constructor 中调用 attachshadow({ mode: 'open' }),且需通过 createelement('style') 注入样式、正确使用 :host 和 ::slotted(),禁止穿透写法及 light dom 渲染。

Shadow DOM 不是“可选的封装技巧”,而是唯一能真正切断样式和 DOM 查询泄漏的原生机制;只要没调用 attachShadow(),所有 class 命名、CSS scoped、BEM 前缀都只是心理安慰。
为什么 attachShadow({ mode: 'open' }) 必须在 constructor 里调用
延迟到 connectedCallback 或首次渲染时再挂载,外部样式可能已注入并计算完毕——污染已经发生,隔离形同虚设。
- 自定义元素实例化后立即执行:
if (!this.shadowRoot) this.attachShadow({ mode: 'open' }); - 重复调用会抛
InvalidStateError,所以必须先判空 -
mode: 'closed'让this.shadowRoot返回null,DevTools 看不到结构,测试无法查询节点,CI 脚本直接失败
往 shadowRoot 注入样式不能靠 innerHTML = '<style>…'</style>
这会让 <style></style> 标签被当作文本节点解析,不进入 CSSOM,后续 JS 修改 sheet.cssRules 完全无效,还可能触发 FOUC(闪屏)。
- 正确做法:用
document.createElement('style')+textContent+shadowRoot.appendChild() - 若需动态更新规则,必须操作该
<style></style>元素的sheet.cssRules,而不是重新赋值innerHTML - 现代方案可用
new CSSStyleSheet()+shadowRoot.adoptedStyleSheets = [sheet],但注意 Safari 16.4+、Chrome 73+ 才稳定支持
:host 和 ::slotted() 是仅有的合法穿透出口
所有其它穿透写法(/deep/、::ng-deep、>>>)已在 Chrome 120+、Firefox 115+ 中彻底移除,继续使用等于主动放弃兼容性。
-
:host只匹配宿主元素自身(如<my-button></my-button>),适合控制容器尺寸、响应属性变化::host([disabled]) { opacity: 0.5; } -
:host-context(.theme-dark)检测祖先链是否有指定 class,用于暗色主题透传;:host(.theme-dark)是常见误用,它只查宿主自身 class -
::slotted(p)仅作用于传入<slot></slot>的顶层<p></p>,且只支持color、font-family、line-height、background-color等继承性属性 - 禁止写
::slotted(*)—— 行为不一致,且把布局责任强行从使用者手里夺走
第三方组件(如 Quill、Foundation)进 Shadow DOM 的安全姿势
直接 render() 到 light DOM 或把 <link rel="stylesheet"> 塞进 shadowRoot.innerHTML,都会导致样式逃逸到全局,污染宿主页面。
- 所有渲染目标必须是
this.shadowRoot,不能是document.body或任意 light DOM 节点 -
<script></script>必须新建节点并appendChild到shadowRoot,且初始化时传入shadowRoot作为查找上下文,例如:new Quill(shadowRoot.querySelector('.ql-editor')) -
<link rel="stylesheet">需代理请求,提取 CSS 内容转成CSSStyleSheet实例,再通过adoptedStyleSheets注入 - 对
@import、相对路径(如url(./icon.svg))、CSS 变量继承断裂等问题,必须提前做资源内联或路径重写
真正难的不是写对 attachShadow,而是让整个团队放弃“加个 class 前缀就安全了”的惯性思维;一旦开始依赖 Shadow DOM,所有样式注入、脚本执行、资源加载都得重走一遍边界校验——漏掉任何一环,隔离就失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











