插值#{$var}是构建BEM库的必要工具,但需配合@at-root、分隔符变量和上下文控制才能正确生成类名;单独使用会导致语义错乱或编译失败。

直接说结论:插值表达式 #{$var} 是构建 BEM 库的必要工具,但不能单独靠它“自动拼类名”;必须配合 @at-root、明确的分隔符变量和严格的上下文控制,否则生成的类名会语义错乱或编译失败。
为什么 &__element 不能直接用在循环里
很多人想在 @each 中写 &__#{$part} 生成 .block__header,结果编译出 .block .block__header(带空格的后代选择器)。这是因为 & 在嵌套块内代表完整父选择器字符串,__ 只是普通文本,Sass 不会把它识别为 BEM 连接符。
- 错误写法:
.block { @each $part in ("header", "footer") { &__#{$part} { } } }→ 输出.block .block__header - 正确做法:用
@at-root #{&}__#{$part} { }提级,确保输出扁平类名 - 更稳妥的方式:提前定义块名变量,如
$block: "card",再写.#{$block}__#{$part} { },完全脱离&的上下文干扰
@each 循环中修饰符列表必须是字符串字面量
BEM 修饰符如 "hover"、"disabled" 是固定字符串,不是变量。如果写成 @each $mod in (hover, disabled)(无引号),Sass 会把 hover 当作未定义变量,报错 Undefined variable "$hover"。
- ✅ 正确:
@each $mod in ("hover", "disabled", "large") { .btn--#{$mod} { } } - ❌ 错误:
@each $mod in (hover, disabled) { .btn--#{$mod} { } } - 从 map 取 key 时也要注意:
map-keys($modifiers)返回的是无引号标识符,需显式转字符串,例如#{map-keys($modifiers)}不行,得用#{nth(map-keys($modifiers), $i)}或改用字符串数组
连字符 - 和双下划线 __ 不是运算符,必须用 + 拼接
SCSS 中 "btn"__"icon" 语法非法;"btn" + "__" + "icon" 才合法。但实际开发中,更推荐用插值拼接,避免手滑漏加引号或 +。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
立即学习“前端免费学习笔记(深入)”;
- ✅ 安全:
.#{$block}__#{$element}(两个插值都是字符串) - ⚠️ 危险:
.#{$block}__#{$element}-active——-active可能被解析为减法,应写成.#{$block}__#{$element}#{"-active"}或.#{$block}__#{$element} + "-active" - 统一管理分隔符:定义
$element-separator: '__'和$modifier-separator: '--',所有拼接都基于变量,避免硬编码和不一致
Mixin 封装 BEM 必须拆开,不能一个函数包打天下
试图用一个 @mixin bem($block, $element: null, $modifier: null) 自动判断生成类名,会导致参数错位、修饰符挂错位置(比如生成 .btn--large__icon)、& 上下文丢失等问题。
- ✅ 推荐结构:
@mixin b($block)声明块作用域,@mixin e($element)只能在b()内调用并产出&__#{$element},@mixin m($modifier)同理产出&--#{$modifier} - 禁止在
e()里嵌套m(),否则违反 BEM 规范(元素修饰符应写成.block__elem--mod,但语义上它仍是元素的变体,不是块的修饰) - 命名空间必须前置:如
$namespace: 'u',所有块名都拼成.#{$namespace}-#{$block},否则库无法跨项目复用
真正难的不是写对一行插值,而是让整套生成逻辑在多人协作、多文件导入、动态主题切换等场景下仍保持类名可预测、可搜索、不冲突。插值只是工具,BEM 的约束力来自结构设计,不是语法糖。

















