本文由大漠根据Marcmintel的《Pushing BEM to the next level with Sass 3.4》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://medium.com/@marcmintel/pushing-bem-to-the-next-level-with-sass-3-4-5239d2371321。
——作者:Marcmintel
——译者:大漠
Sass3.4增加了对父选择器的处理,主要是帮助你更好的处理选择器。我想这是为了更好的使用mixins
来定义BEM。
.test {
@debug type-of(&); //打印出.test
}
.test li a{
$selector: &;
test: $selector;//打印出.test li a
}
Sass3.3中父选择器的问题
自从选择器能结合任何字符串时,父选择器在Sass3.3中就可以正常工作。这篇文章很好的阐述了BEM在Sass3.3能很好的运行,这也是其新特性之一。
虽然下面这段代码是有效的,但是直到现在都无法通过mixins
来调用类:
.block {
&__element {
background: green;
}
}
这样照样不能正常运行:
@mixin element($selector) {
#{&}__element {
@content;
}
}
.block {
@include element(element){
//mixin ‘element’ 依旧不能连接其父选择器
}
}
你无法创建一个mixins
,让block
成为element
和modifier
的前缀,而且element
和modifier
也不知道他们自己是围绕着哪个block
。
在Sass3.4中改进父选择器的可能性
在Sass3.4中打印出来的选择器可以是一个列表。所以我们可以这样使用mixins
:
$elementSeparator: "__";
$modifierSeparator: "--";
@mixin b($block) {
.#{$block} {
@content;
}
}
@mixin e($element) {
@at-root {
#{&}#{$elementSeparator + $element} {
@content;
}
}
}
@mixin m($modifier) {
@at-root {
#{&}#{$modifierSeparator + $modifier} {
@content;
}
}
}
接下来可以这样使用:
@include b(test) {
background: red;
@include e(element) {
font-size: 14px;
@include m(big) {
font-size: 18px;
}
};
@include m(modifier) {
color: blue;
}
}
//output CSS
.test {
background: red;
}
.test__element {
font-size: 14px;
}
.test__element--big {
font-size: 18px;
}
.test--modifier {
color: blue;
}
然而我还遇到几个问题。第一个问题就是在修饰符modifier
中嵌套一个元素element
。例如下面的这段Sass代码:
@include b(test) {
background: red;
@include m(modifier) {
color: blue;
@include e(subelement) {
background: gray;
}
}
}
//output CSS
.test {
background: red;
}
.test--modifier {
color: blue;
}
.test--modifier__subelement {
background: gray;
}
这其实不是我想要的结果。实际上我想在修饰符下面嵌套一个元素(如.test--modifier .test__subelement
)。我实现这样的目标,我们可以检测$modifierSeparator
是不是含有选择器字符串('— —')。如果存在,不想添加更多的后缀,改成一个嵌套选择器。实现这个可以创建一个函数来检测。
如果父选择器返回的是列表类型,需要先将其转换成一字符串。下面的函数就是用来做这件事:
@function selectorToString($selector) {
$selector: inspect($selector); //cast to string
$selector: str-slice($selector, 2, -2); //remove bracket
@return $selector;
}
接下来把selectorToString()
函数放在containsModifier()
函数中,做一些字符串的处理:
@function containsModifier($selector) {
$selector: selectorToString($selector);
@if str-index($selector, $modifierSeparator) {
@return true;
}
@else {
@return false;
}
}
除此之外,还需要在block
选择器外做分割,因为我们是想在block
中追加element
而不是modifier
:
@function getBlock($selector) {
$selector: selectorToString($selector);
$modifierStart: str-index($selector, $modifierSeparator) — 1;
@return str-slice($selector, 0, $modifierStart);
}
将他们都放到一个函数,他接收的是一个选择器,并且检测其是否含有一个modifier
,然后匹配对应选择器:
@mixin e($element) {
$selector: &;
$block: getBlock($selector);
@if containsModifier($selector) {
@at-root {
#{$selector} {
#{$block+$elementSeparator+$element} {
@content;
}
}
}
}
@else {
@at-root {
#{$selector+$elementSeparator+$element} {
@content;
}
}
}
}
这个时候使用下面的代码:
@include b(test) {
background: red;
@include m(modifier) {
color: blue;
@include e(subelement) {
background: gray;
}
}
}
编译出来的CSS代码:
.test {
background: red;
}
.test--modifier {
color: blue;
}
.test--modifier .test__subelement {
background: gray;
}
最后的代码如下:
$elementSeparator: '__';
$modifierSeparator: '--';
@function containsModifier($selector) {
$selector: selectorToString($selector);
@if str-index($selector, $modifierSeparator) {
@return true;
} @else {
@return false;
}
}
@function selectorToString($selector) {
$selector: inspect($selector); //cast to string
$selector: str-slice($selector, 2, -2); //remove brackets
@return $selector;
}
@function getBlock($selector) {
$selector: selectorToString($selector);
$modifierStart: str-index($selector, $modifierSeparator) - 1;
@return str-slice($selector, 0, $modifierStart);
}
@mixin b($block) {
.#{$block} {
@content;
}
}
@mixin e($element) {
$selector: &;
@if containsModifier($selector) {
$block: getBlock($selector);
@at-root {
#{$selector} {
#{$block+$elementSeparator+$element} {
@content;
}
}
}
} @else {
@at-root {
#{$selector+$elementSeparator+$element} {
@content;
}
}
}
}
@mixin m($modifier) {
@at-root {
#{&}#{$modifierSeparator+$modifier} {
@content;
}
}
}
实际运用如下:
@include b(block) {
background: red;
@include e(header){
font-size: 14px;
@include m(css) {
font-size: 18px;
}
};
@include m(book) {
color: blue;
@include e(kindlebook) {
background: gray;
}
}
}
编译出来的CSS:
.block {
background: red;
}
.block__header {
font-size: 14px;
}
.block__header--css {
font-size: 18px;
}
.block--book {
color: blue;
}
.block--book .block__kindlebook {
background: gray;
}
总结
在Sass3.3中可以很容易使用BEM,但会有一定的限制性,如今在Sass3.4中可以说BEM的使用更简单。可以让你的代码量很小,简洁易懂,而且还易于维护。我希望我的这个思路能给你带来一定的灵感,让给你带来方便,更希望你也能在些基础上创新。
如果你想自己动后写一遍,你可以参照示例码一回,也可以直击@Sassmeister。
如果你有任何意或者想法,希望与我一起探讨,我会非常感激您。
译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!
如需转载,烦请注明出处:
英文原文:https://medium.com/@marcmintel/pushing-bem-to-the-next-level-with-sass-3-4-5239d2371321
中文译文:http://www.w3cplus.com/preprocessor/pushing-bem-to-the-next-level-with-sass-3-4.html