
Shadow DOM 是什么,不是什么
它不是新语法或框架特性,而是浏览器原生的 DOM 分离机制。核心作用是让一段 HTML、CSS 和 JS 在逻辑上“自包含”——外部样式进不去,内部样式出不来,querySelector 默认查不到里面节点。
常见错误现象:document.querySelector('.my-button') 找不到影子树里的按钮;给 body 写的全局 CSS 没影响到组件内部文字颜色。
使用场景:Web Components 封装可复用 UI(如自定义 my-modal)、避免样式冲突、实现样式作用域隔离。
如何创建一个基本 Shadow DOM
必须通过 attachShadow() 方法挂载,不能直接写 HTML 或用模板字符串硬塞。它返回一个 ShadowRoot 对象,后续操作都得在这个根上进行。
立即学习“前端免费学习笔记(深入)”;
实操建议:
-
element.attachShadow({ mode: 'open' })是最常用方式,允许 JS 从外部访问影子树;设为'closed'后,element.shadowRoot返回null,调试困难,慎用 - 不要在影子根里直接写
<style></style>标签再 innerHTML 插入,应使用appendChild(document.createElement('style'))或构造CSSStyleSheet - 影子树不继承父级的
font-family、color等属性,需显式重置或使用inherit
样式穿透和跨边界通信怎么处理
Shadow DOM 的样式隔离是默认行为,但不是铁壁。外部样式想影响内部,只能靠 ::slotted()、:host、:host-context() 这几个伪类,且支持度有限;内部想暴露接口给外部,得靠自定义事件或公开方法。
容易踩的坑:
-
:host(.active) { ... }只能匹配宿主元素自身 class,不能匹配其子元素的 class -
::slotted(*)只能选中被<slot></slot>投影进去的内容,对影子树内原生节点无效 - 用
dispatchEvent(new CustomEvent('change', { composed: true }))才能让事件冒泡出 Shadow DOM;漏掉composed: true,外部监听就收不到
兼容性和性能要注意什么
所有现代浏览器都支持 attachShadow,但 IE 完全不支持,连 customElements.define 都不行。如果要兼容旧环境,得用 polyfill(如 @webcomponents/webcomponentsjs),但它无法完美模拟样式隔离。
性能方面,每个 ShadowRoot 都有独立样式计算和布局上下文,嵌套过深或频繁切换 mode 会增加开销;大量使用 slot 且内容复杂时,重排可能变慢。
关键提醒:Shadow DOM 不等于 Web Components 全套,它只是其中一环。没用 customElements.define 注册,光有 shadowRoot 也只是个隔离容器,不会自动升级、不响应生命周期钩子。











