styled-components中不能直接使用&__element语法,因其css解析器仅支持单层&父选择器,bem类名需由js通过clsx+attrs在组件props层生成并注入classname,确保可调试、可搜索且语义对齐。

styled-components里不能直接写&__element,得靠JS逻辑补位
因为styled-components不支持Sass那种&__text语法,你写&__text会被编译成.ComponentName__text——但这个类名根本不会被自动加到子元素上。它只管自己组件的根节点,子元素的class得你自己传进去。
常见错误现象:在styled.button里写&__icon,结果图标没样式;或者以为asChild能透传BEM结构,实际只是替换渲染标签,不带class逻辑。
- 把BEM块名定义为常量,比如
const BLOCK = 'button' - 用
clsx组合元素和修饰符:clsx(`${BLOCK}__icon`, { [`${BLOCK}__icon--left`]: iconPosition === 'left' }) - 通过
attrs把拼好的class注入根节点,再靠React.cloneElement或children透传到子元素(如果需要)
修饰符状态必须映射为props,别依赖DOM属性
[disabled]在React里经常不生效,因为disabled是prop,不是真实DOM属性;而button--disabled这种BEM修饰符是你主动控制的class,和组件state严格同步,SSR也能正确输出。
使用场景:按钮加载中、表单校验失败、卡片置顶状态等需要显式标记的UI状态。
- 修饰符命名要反映语义,比如
button--loading✅,button--gray❌(视觉描述易过时) - 布尔状态建议用
--is-前缀:modal--is-open,避免modal--open和modal--opened歧义 - 多个状态不要硬拼复合修饰符:
button--disabled-loading❌,应保持独立:button--disabled button--loading✅
Tailwind项目里保留一个BEM锚点类,别全扔给utility class
满屏flex items-center p-4 text-sm rounded看着快,但搜索定位困难、重构成本高、设计师也看不懂结构。BEM在这里不是写样式,而是划边界。
性能影响几乎为零,但可维护性差别巨大:一个product-card类能让你grep到所有相关JS/CSS/测试,而一堆utility class只能靠肉眼扫。
- 在最外层容器加一个BEM块名,比如
class="product-card bg-white" - 用
@layer components封装该块的基础布局:@apply flex flex-col gap-3 p-4; - 元素级样式仍用Tailwind,但通过BEM类名做语义分组,比如
<h3 class="product-card__title text-lg font-bold"></h3>
动态插入DOM时,BEM类名不会自动生效,得手动挂载
CSS是静态解析的,浏览器不会因为新节点有card__header就去匹配已有规则——前提是那个class真被加进去了,且对应CSS已加载。
容易踩的坑:用innerHTML拼接字符串时漏掉双下划线,写成card_header;或用document.createElement('div')后忘记classList.add('card__header')。
- 永远用
classList.add()而非className =拼接,避免覆盖已有class - 确保BEM CSS资源在动态操作前已加载,别等JS执行完才
import样式文件 - 框架内优先走组件化渲染(如React.createElement),让BEM类名由JSX统一生成,而不是靠JS后期修补
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











