shadow dom 仅创建样式与 dom 隔离边界,不自动优化封装;封装强度取决于结构组织、样式注入方式、接口透传及盒模型/继承的显式约束。

Shadow DOM 本身不自动优化封装边界,它只创建一个样式与 DOM 的隔离边界;真正的封装强度取决于你如何组织结构、注入样式、透传接口,以及是否主动约束盒模型和继承行为。
attachShadow() 是边界创建入口,不是开关
调用 attachShadow() 不是“打开封装”,而是新建一棵独立的 shadow tree——它自带专属 CSSOM、事件流和样式匹配上下文。浏览器在计算样式时,压根不会把外部 CSS 规则(哪怕带 !important)纳入该树的匹配队列。
- 不调用
attachShadow(),所有内容都在 light DOM,所谓“组件”只是 class 命名游戏,button { color: red }依然能穿透 -
mode: 'closed'下element.shadowRoot永远返回null,这是规范强制设计,不是 bug;调试时请用'open' - 重复调用会抛出
DOMException: Shadow root cannot be created on a node already hosting a shadow root. - 一个元素只能有一个 shadow root,无法叠加或嵌套多个
:host 和 ::slotted() 是唯一合法的穿透通道
Shadow DOM 不是黑箱,而是有明确定义的“接口缝”:只有 :host 能响应宿主自身状态(如属性、类名),只有 ::slotted() 能有限影响 slot 分发的内容——且仅支持可继承属性(color、font-family、line-height、background-color)。
-
:host([disabled]) { opacity: 0.5; }合法;但:host .inner-btn无效,因为子选择器不穿透 -
::slotted(p) { line-height: 1.6; }有效;但::slotted(*)行为不一致,已被 Chrome/Firefox 标记为不推荐 -
:host-context(.theme-dark)已被弃用,应改用:host(.theme-dark)+ 外部 class 控制宿主 - slot 内容的
margin、display、flex等布局责任不在组件内,不能靠::slotted()强制重置
样式注入必须走 DOM 方法,不能拼字符串
把 <style></style> 直接写进 shadowRoot.innerHTML = `<div></div>
<style>...</style>` 是常见错误:此时 <style></style> 标签被当普通 HTML 解析,不进入 CSSOM,后续 JS 动态修改(如 sheet.cssRules[0].style.borderColor)完全失效,还可能触发 FOUC。
- 正确做法是分两步:
shadowRoot.appendChild(document.createElement('style')).textContent = '...'; - 若需复用基础样式(如字体重置、变量定义),可用
new CSSStyleSheet()预建,再赋给shadowRoot.adoptedStyleSheets(注意 Safari 16.4+、Chrome 73+ 才稳定支持) - 避免在
<style></style>中插值变量,易引入 XSS;动态主题切换应通过 CSS 自定义属性 +:host类名控制 -
<link rel="stylesheet">在 shadow 内完全不生效,除非用adoptedStyleSheets
盒模型和继承必须显式约束
Shadow DOM 不改变盒模型规则,默认仍是 box-sizing: content-box;也不自动继承 font-family 或 color —— 这些都得手动写进 :host 或子元素规则里,否则组件文字可能突然变小、颜色错乱。
- 统一设
:host, :host * { box-sizing: border-box; },防止 slot 内容意外撑开容器高度 - 显式继承基础样式:
:host { font-family: inherit; color: inherit; },否则外部对 shadow 内无影响 -
:host上慎用margin,外部调用者可能已设 margin,叠加导致间距失控;优先用padding或 slot 分配留白 - slot 内容若带
display: block且无box-sizing,其padding会撑大组件高度,而:host无法重置——必须在上游或 slot 层统一规范
最容易被忽略的是边界处的隐式依赖:比如用 ResizeObserver 监听 :host 尺寸时,拿到的是含 border 和 padding 的 contentRect,但若内部元素用了 box-sizing: content-box,计算逻辑就对不上;这类细节不会报错,只会让组件在组合场景中悄悄错位。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











