从 Element UI 的 Theme 源码来看命名规范 BEM 的应用

haiweilian2020-01-21前端CssScss

最近自己在写自己的博客页面,想着就参考框架的组织结构,自己是对 element-ui 是比较熟悉的,就拿来参考了。element-uiopen in new window 的样式是使用 scss 预处理器,和 BEM 思想写的。整体来说组织的还是挺好的,从中可以学到一些封装思想,可以提升一下自己。

目录结构

你可以把theme-chalkopen in new window的源码下载下来,以便对比着看。下面是几个比较重要的文件(2019-12-21 最新版本为 v.2.13.0)。

├── gulpfile.js // 打包配置,可以把代码压缩 cssmin() 注释掉,以便查看编译后的代码。
├── lib // 打包文件存放目录。
├── src
│   ├── common
│   │   └── var.scss // 全局变量,大小、颜色。
│   ├── mixins
│   │   ├── config.scss // 定义了BEM的分隔符。
│   │   ├── function.scss
│   │   ├── mixins.scss // 主要定义了实现BEM类,也和 function.scss 相关联。
│   │   └── utils.scss
│   ├── radio-group.scss
│   ├── radio.scss
│   ├── ...其他具体的组件...
└──

BEM 支持

源码中最重要的一个概念 BEM ,贯穿全局。

BEM 分割符

src/mixins/config.scss 定义了命名空间以及块、元素、修饰的分割符。了解过 BEM 的大概都知道,BEM 最最重要的是思想,而分隔符可以按照一定的规范去定义,这里不再多说了。

命名空间和块。框架都会选择一个相对应的前缀,避免样式冲突,如:.el-input

$namespace: "el";

元素。使用的 __  双下划线,如: .el-input__inner

$element-separator: "__";

修饰。使用的 -- 双连字符,如:.el-input--small

$modifier-separator: "--";

状态。使用的 is- 作为前缀,如:.el-input.is-disabled

$state-prefix: "is-";

BEM Mixin

src/mixins/mixins.scss 定义了 @mixin b($block)@mixin e($element)@mixin m($modifier)@mixin when($state) 几个主要的 mixin。 用一个源码举例,src/input.scss 涉及到了所有的方法,也比较常用。

mixin 中涉及到的几个知识点:

@mixin b($block)

定义生成块。参数为块的名称。

@include b(input) {
  display: inline-block;
}

编译后。

.el-input {
  display: inline-block;
}

@mixin e($element)

定义生成元素。参数是元素的名称,可以传入多个,($element1, $element2, ...)。mixin 中的 @if hitAllSpecialNestRule 可以先跳过,下面单说。

@include b(input) {
  @include e(inner) {
    padding: 0 15px;
  }

  @include e((suffix, suffix-inner)) {
    position: absolute;
  }
}

编译后。

.el-input__inner {
  padding: 0 15px;
}

.el-input__suffix,
.el-input__suffix-inner {
  position: absolute;
}

@mixin m($modifier)

定义生成修饰。参数是修饰的名称,可以传入多个,($modifier1, $modifier2, ...)

@include b(input) {
  @include m(medium) {
    height: 30px;
  }

  @include m((mini, small)) {
    height: 20px;
  }
}

编译后。

.el-input--medium {
  height: 30px;
}

.el-input--mini,
.el-input--small {
  height: 20px;
}

@mixin when($state)

定义条件状态。参数是状态的名称。

@include b(input) {
  @include when(disabled) {
    cursor: not-allowed;
  }
}

编译后.

.el-input.is-disabled {
  cursor: not-allowed;
}

BEM Function

@mixin e 里面有一个判断 @if hitAllSpecialNestRule($selector),它的作用是判断什么情况下使用选择器层级嵌套。

bem 的规范中,推荐的是选择器层级尽量平级,减少嵌套 所以也就是在上面的 mixin 中都使用了 @at-root 规则 。 而实际中我们不可避免使用嵌套,在不同的场景下实现样式覆盖。如在不同大小,和不同状态我们是不是应该给下级设置不同的大小和颜色。

function 涉及到的几个知识点:

containsModifier($selector)

在修饰符之下嵌套元素。

@include b(input) {
  @include m(medium) {
    @include e(inner) {
      padding: 0 10px;
    }
  }
}

编译后。

.el-input--medium .el-input__inner {
  padding: 0 10px;
}

containWhenFlag($selector)

在状态下嵌套元素。

@include b(input) {
  @include when(disabled) {
    @include e(inner) {
      color: #eeeeee;
    }
  }
}

编译后。

.el-input.is-disabled .el-input__inner {
  color: #eeeeee;
}

containPseudoClass($selector)

在伪类下嵌套元素。

@include b(input) {
  &:hover {
    @include e(inner) {
      border: 1px solid blue;
    }
  }
}

编译后。

.el-input:hover .el-input__inner {
  border: 1px solid blue;
}

hitAllSpecialNestRule($selector)

以上三种情况都会在特定场景下生效。最后通过或者 or 判断。

参考文献

使用 scssbem 可以很好的组织我们的 css 结构,推荐学习。源码中还有栅格布局row.scsscol.scss 文件写的也很简洁,我现在项目就是采用的这一套生成的。

https://sass-lang.com/documentationopen in new window

https://en.bem.info/methodology/quick-startopen in new window

最后更新时间 11/10/2023, 1:24:17 AM