Web Components 中 Shadow DOM 天然隔离 BEM 类名,BEM 样式须写在 <style> 或 adoptedStyleSheets 中;Block 名须与自定义元素名语义一致,Element 和 Modifier 需严格遵循命名规范,JS 操作类名必须基于 this.shadowRoot,构建时需校验模板字符串中的 BEM 类名。

Web Components 的 Shadow DOM 天然隔离 BEM 类名
Shadow DOM 的样式作用域默认不穿透,BEM 的 class 名不需要额外封装就能避免全局污染。你写 .card__title,它只在组件内部生效,不会和页面其他 .card__title 冲突——这不是靠命名“防撞”,而是浏览器原生保障的隔离机制。
常见错误现象:开发者在 :host 里写 .card { display: block; },结果发现外部样式(比如重置 CSS)意外透入。这是因为 :host 不自动阻止继承,而 BEM 类名本身不解决继承问题。
- 所有 BEM 样式必须写在
<style>标签内或adoptedStyleSheets中,不能依赖外部注入的全局 CSS -
:host只用于控制组件容器自身(如尺寸、显示方式),不要在里面塞.card__header这类选择器——它不属于宿主,属于 Shadow DOM 内部 - 若需响应外部主题(如
theme="dark"),用:host([theme="dark"]) .card__title,而非在 JS 中动态加类
BEM Block 名必须与 Custom Element 名保持语义一致
自定义元素名(如 <user-card>)就是 Block 名的自然映射,不能拆成 user-card 和 user__card 两套命名。否则 HTML 里写 <user-card class="user__card">,既冗余又破坏 BEM 的“块即组件”原则。
使用场景:当组件需要支持 SSR 或被非 JS 环境渲染时(如静态预览),HTML 中的 class 名必须和 JS 定义的元素名逻辑对齐,否则构建工具无法正确提取样式。
立即学习“前端免费学习笔记(深入)”;
- Block 名 = 自定义标签小写+短横线:✅
<search-form>→ CSS 中用.search-form,❌<SearchForm>或<searchform> - Element 名必须是 Block 的直系子语义:✅
.search-form__input、✅.search-form__submit,❌.search-form__input-field(field是冗余词) - Modifier 不应重复表达 Web Components 已有的属性:❌
.search-form--disabled+disabled属性共存;✅ 用属性直接驱动状态:[disabled] .search-form__input { opacity: 0.5; }
JS 动态操作类名时,BEM 工具函数必须运行在 Shadow Root 内
在 Web Components 中调用 element.classList.add("button--primary") 没问题,但如果你用封装的 BEM 工具函数(如 cn("button", "primary")),它生成的类名必须被应用到 Shadow DOM 内的元素上,而不是 light DOM 节点。
容易踩的坑:工具函数返回 "button--primary",但你把它加到了 document.querySelector("button") 上——这个按钮根本不在当前组件的 Shadow DOM 里,样式不会生效。
- 所有类名操作必须基于
this.shadowRoot查询:✅this.shadowRoot.querySelector(".button__label").classList.add(this.cn("label", "hidden")) - 禁止在 connectedCallback 中直接操作 light DOM 子节点的 class,除非明确使用
slot并监听slotchange - 如果组件允许用户传入带 BEM 类的 HTML(如通过
<template slot="footer"><div class="card__footer"></div></template>),确保这些类名在 light DOM 中也符合当前 Block 命名空间,否则无法被 Shadow 内样式匹配
构建时 BEM 校验必须覆盖 Shadow DOM 模板字符串
很多团队用 stylelint-selector-bem-pattern 检查 CSS,但它默认不扫描 JS 中的模板字面量。如果你在 render() 方法里写 html`<div class="card__body">...</div>`,拼错成 "card__boday",CI 不会报错。
性能影响:未校验的错误类名会导致样式缺失,但浏览器不会提示,只能靠人工 QA 或截图比对发现,上线后才暴露。
- 用 ESLint 插件(如
eslint-plugin-lit)配合正则规则检查html``和svg``字符串中的 BEM 类名格式 - 禁止在模板中硬编码修饰符值:❌
class="button--${variant}",✅ 提前映射为合法修饰符:const mod = { primary: "primary", danger: "danger" }[variant] || "primary" - 若使用 Lit,启用
lit-a11y和lit-no-invalid-aria同步校验,因为 ARIA 属性常和 BEM Modifier 语义耦合(如aria-disabled应对应button--disabled)
.button__icon,却忘了在 CSS 中定义它,这种错误不会触发任何构建警告,只会在某个尺寸下突然丢失图标。


















