🧐【记1次探索】如何完善BEM命名方式的缺陷、Scss中我们该如何选择@mixin和@extend

1 BEM命名方式的探索

1-1背景

编写语义化 的html是现代化专业前端开发的原则之一,但是尽管在html中存在部分语义化标签,但是只能覆盖极小一部分的语义化要求,多数情况下仅仅通过html标签的语义,我们仍然无法判断它所代表的页面内容是哪一部分。

为了解决这一问题,我们决定从html标签的类名入手,因为在此之前类名很少或根本没有向开发者传递有用的语义信息,仅仅是作为CSS和JS的钩子一般,用来让CSS和JS去匹配标签。

因为css类名 本身是没有任何语义化的含义,所以我们需要一种组织方式去命名我们的css类名,赋予它语义化 的含义,因此Yandex团队提出了现在知名的B__E--M命名方式。

1-2 😞我所遇到的问题

我们知道BEM的命名方式中,原则上是保证各个部分只有一级 B__E--M ,修饰器需要和对应的块或元素一起使用,避免单独使用。因此当我发现这种只有一级 B__E--M 的原则在面对超过2层以上嵌套的html结构时,就会出现类名在表达hlmt结构上的一些不清晰,例如:

单级的"B__E--M

html 复制代码
<!-- HTML 结构 --> 
<div class="card"> 
    <div class="card__wrap"> 
        <div class="card__info"> 
            <h1 class="car__heading">标题</h1> 
            <p class="car__text">文本</p> 
            <button class="car__btn--primary"><button> 
        </div> 
    </div> 
</div>

上面这个例子中,我仅从类名很容易误认为wrap、info、text和btn它们的html结构是同级的

所以我在想有没有什么方式可以完善这一部分缺陷,并且保留BEM 原本命名方式的优点

1-3 BEM命名方式的探索

探索部分1: 起初当我面对只有一级 B__E--M 无法清晰的表达2层以上的html嵌套结构这个问题时,我第一想法是 "为什么不能采用多级的B__E--M"命名方式呢? , 但是通过对比这两种方式,我知道了为什么宁可牺牲一部分的层级表达 ,也不采用多级的方式,通过下面这个例子来说明一下。

多级的"B__E--M

xml 复制代码
<!-- HTML 结构 -->
<div class="card">
  <div class="card__wrap">
       <div class="card__wrap__info">
           <h1 class="car__wrap__info__heading">标题</h1>
           <p class="car__wrap__info__text">文本</p>
           <button class="car__wrap__info__text--primary"><button>
       </div>
  </div>
</div>

<!-- Scss 样式 -->
.card {
    &__wrap{
        &__info{
            &__heading{}
            &__text{}
            &__button{
                &--primary{}
            }
        }
    }
}

我们可以看到采用多级B__E--M去命名我们的类名,最终导致了:

  • html 结构中出现了超长的 类名。
  • 样式 部分出现了超深的 嵌套(使用scss预处理器的情况)。
  • 如果这里使用原生的css书写 样式 部分,我们不可避免的加重了BEM命名方式 的缺点,出现了 超长的 类名名选择器。

综上,多级的B__E--M 命名会大大的降低我们代码的可读性,从而也降低了可维护性,但是我们不可否认的是 多级的B__E--M 命名也极大的保持了我们 html结构 的清晰性,取舍下来所以仍然不推荐使用这种方式!

1-3-1 如何解决 "单级"B__E--M的问题

我们知道 "单级"B__E--M 目前的缺憾就是面对多于2层嵌套结构的hmtl,由于只能保持一级,所以无法从类名上清晰的表示html的结构,也就是无法从类名上判断,是同级元素,还是后代元素。

通过上面 "单多级"B__E--M 的比较,我们发现两者各有优缺点,通过取舍果断的选择了 "单级"B__E--M, 虽然你很好😏,但是我想要更好的!

所以我在想 "究竟还有没有更好的方式可以解决这个问题",通过大量的搜索和阅读,功夫不负有心人😘,在这篇文章关于HTML语义和前端架构------尼古拉斯·加拉格尔 (nicolasgallagher.com)中我终于找到了启发性的部分!

探索部分2 : 结合单级BEM 我发现我们们可以用 -单划线 来表示元素的后代范式element-descendant,例如:

xml 复制代码
<!-- HTML 结构 -->
<div class="card">
  <div class="card__wrap">
       <div class="card__wrap-info">
           <h1 class="car__info-heading">标题</h1>
           <p class="car__info-text">文本</p>
           <button class="car__info-btn car__info-btn--primary"><button>
       </div>
  </div>
</div>

<!-- Scss 样式 -->
.card {
    &__wrap {}
    &__wrap-info{}
    &__info-heading{}
    &__info-text{}
    &__info-btn{
        &--primary{}
    }
}

这个例子就完美的解决了,单级B__E--M 的问题,通过类名我们可以清晰的知道card为块, wrap为元素 ,info为wrap的后代, heading、text和btn为 info的后代。

上面的例子像我们表明了,通过范式element-descendant这种方式可以解决我们单级B__E--M 的问题,但是还是可以发现一些 瑕疵🧐 ,就是我们的button按钮,由于采用了 "双类" 的模式来作为button的类选择器,可以发现在html结构中,这一部分的类名显得 特别长, 其实在实际开发中像按钮这种元素,都会使用通用类的样式。例如:.btn .btn-primary ,但是这样命名无法清晰的表明这种通用样式的来源 ,所以为了解决这个问题,我结合BEM命名的方式和 "-单划线" 来实现。

探索部分3 : 我们可以用 -单划线 来表示前缀,类似命名空间一样的东西,范式:prefix-classname,拿这里的按钮举例:

xml 复制代码
<!--_common.scss文件中-->
.c-btn{
    &--primary{}
    &--info{}
    &--danger{}
}

<!-- HTML 结构 -->
<div class="card">
  <div class="card__wrap">
       <div class="card__wrap-info">
           <button class="c-btn c-btn--primary"><button>
       </div>
  </div>
</div>

这个示例中,c (common的首字母)作为前缀,表明了btn,以及相关的变体,都是属于这个通用样式的命名空间里的样式类,同样变体部分我也统一的使用了BEM中的表示方法,以后在书写公共样式的时候就可以采用这种命名方式!

2 Scss中@mixin和@extend的探索

Mixins允许我们在项目中复用样式片段 ,可以传递参数这个特性使得它们非常灵活,强大。同样,我们也可以使用@extend命令让一个选择器继承其它选择器去复用样式片段 。有的时候Mixinextend好像做了同样的事情,那我们应该选择哪一个呢?

@mixin和@extend的区别:

  1. @minin可以传递参数

  2. @minin不会产生DRY式的代码,@extend会产生DRY式的代码,例如:

    • 采用@extend的方式。
    less 复制代码
        // scss代码
        .button {  
            background: green;  
        }
    
        .button-1 {  
            @extend .button;  
        }
    
        .button-2 {  
            @extend .button;  
        }
    
        // 编译后的css代码
        .button, 
        .button-1, 
        .button-2 {  
            background: green;  
        }
    • 采用@mixin的方式。
    less 复制代码
      // scss代码
      @mixin button {  
          background-color: green;  
      }
    
      .button-1 {  
          @include button;  
      } 
    
      .button-2 {  
          @include button;  
      }
    
      // 编译后的css代码
      .button {  
          background-color: green;  
      }
    
      .button-1 {  
          background-color: green;  
      }
    
      .button-2 {  
          background-color: green;  
      }
      ```

综上,可以发现采用 @mixin的方式,相同的样式片段在不同的选择器中重复多次,这也导致了编译出来的CSS不是DRY形式的。

这可能会暗示你应该一直使用@extend,但是@extend也有一些缺点,@extend命令不够灵活。不能向它传递参数,它原本是啥样就是啥样。

@mixin主要的优势就是它能够接受参数。如果想传递参数,你会很自然地选择@mixin而不是@extend

如果没有任何参数,使用@extend来创造DRY应该是个不错的选择。

综上,可以作为两者之间选择的一个依据,但是我认为应该还有一下其它更深的点,可以作为选择的依据,但是目前我就探索出了这些点,可能还需要更多的实践来总结吧💪

相关推荐
雯0609~18 分钟前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ21 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z26 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
彭世瑜1 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4041 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five1 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序1 小时前
vue3 封装request请求
java·前端·typescript·vue
临枫5411 小时前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
前端每日三省1 小时前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript