BEM 在 Sass 3.4 中的提升

发布于 2021-11-11 23:24:55 字数 6539 浏览 1334 评论 0

Sass3.4 增加了对父选择器的处理,主要是帮助你更好的处理选择器。我想这是为了更好的使用 mixins 来定义 BEM

.test {
    @debug type-of(&); //打印出.test
}

.test li a{
    $selector: &;
    test: $selector;//打印出.test li a
}

Sass 3.3 中父选择器的问题

自从选择器能结合任何字符串时,父选择器在 Sass3.3 中就可以正常工作。这篇文章很好的阐述了BEM在Sass3.3能很好的运行,这也是其新特性之一。

虽然下面这段代码是有效的,但是直到现在都无法通过 mixins 来调用类:

.block {
    &__element {
        background: green;
    }
}

这样照样不能正常运行:

@mixin element($selector) {
    #{&}__element {
        @content;
    }
}
.block {
    @include element(element){
        //mixin ‘element’ 依旧不能连接其父选择器
    }
}

你无法创建一个mixins,让block成为elementmodifier的前缀,而且elementmodifier也不知道他们自己是围绕着哪个block

在 Sass 3.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的使用更简单。可以让你的代码量很小,简洁易懂,而且还易于维护。我希望我的这个思路能给你带来一定的灵感,让给你带来方便,更希望你也能在些基础上创新。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

晚风撩人

暂无简介

0 文章
0 评论
24510 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文