Shadow DOM 是唯一真正隔离样式的机制,启用需调用 attachShadow({mode: 'open'});外部样式默认无法穿透,内部样式不泄露;需手动透传CSS变量、正确监听属性变更、合理使用 :slotted() 控制插槽样式,并严格遵守自定义元素命名规范。

Shadow DOM 是唯一能真正隔离样式的机制,不启用它,所谓“封装”只是心理安慰。
为什么不用 Shadow DOM 就谈不上样式封装
自定义标签若只靠 class 名或 scoped CSS 模拟隔离,外部样式仍可能穿透(比如 .my-button:hover 或全局 button { color: red }),而 Shadow DOM 会建立一个样式边界:内部样式不会泄露,外部样式默认进不去。
- 必须显式调用
this.attachShadow({ mode: 'open' }),否则所有样式都在 light DOM,毫无隔离可言 -
mode: 'closed'会阻止 JS 通过el.shadowRoot访问,调试困难,日常开发推荐'open' - Shadow DOM 内部无法直接继承外部字体、颜色等 CSS 变量,需手动透传,例如用
:host { --text-color: var(--text-color, #333); }
attributeChangedCallback 不触发样式更新?检查属性监听配置
样式响应属性变化(如 disabled 切换按钮灰态)不能靠 JS 直接改 this.disabled = true,必须走 attribute 通道,且需声明监听。
- 静态定义
static get observedAttributes() { return ['disabled', 'size']; },漏写就收不到变更 - HTML 中写
<my-button disabled></my-button>或 JS 调用el.setAttribute('disabled', '')才会触发attributeChangedCallback - 首次渲染时的属性值不会进该回调,要在
constructor或connectedCallback里用this.hasAttribute('disabled')补读 - 在回调里别直接操作
shadowRoot.innerHTML全量重写,应只更新 class 或 style 属性,避免丢掉用户插入的<slot></slot>内容
slot 插槽内容被外部样式污染?用 :slotted() 和 encapsulation 规则
<slot></slot> 是内容分发点,但它本身不带样式隔离——插进去的 <span>文本</span> 仍受外部 CSS 影响。要限制这种穿透,得靠 Shadow DOM 的选择器规则。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
立即学习“前端免费学习笔记(深入)”;
- 在 shadowRoot 的
<style>里用:slotted(*) { all: initial; }重置所有继承样式(慎用,可能清掉 font-family) - 更稳妥的是限定范围:
:slotted(h2), :slotted(p) { margin: 0; color: inherit; } - 不要在 slot 外层套 div 再加 class 去控制样式,那等于把样式责任推给使用者;应在组件内部用
:host ::slotted(*)主动约束 - 如果用户传入的是带 class 的复杂结构,且你无法预设其样式行为,那就明确文档里写:“本组件不保证 slot 内第三方组件的样式兼容性”
customElements.define() 报错 “not a valid custom element name”?连字符不是可选项
注册失败基本就一个原因:标签名不符合规范。浏览器校验极严,拼写、大小写、符号全错不得。
- 必须含且仅含一个连字符,如
my-button、user-card;mybutton、MyButton、my_button、1button全报错 - 不能以数字开头,不能含大写字母或下划线,不能是已保留名(如
html、script) - 重复注册同一名字会静默失败(不报错但不生效),上线前建议加一层判断:
if (!customElements.get('my-button')) customElements.define('my-button', MyButton) - 开发中热更新时,旧定义未清除会导致注册失败,刷新页面是最简单解法
最难的不是让样式不出界,而是当用户既想复用你的组件、又想微调某处边距或颜色时,你得提前留好 CSS 自定义属性出口,比如 --my-button-padding,而不是事后补一堆 !important 或开放 ::part() ——后者兼容性还差。


















