先上车

当着手开始打造一套属于自己的组件样式时,就要先了解elementUI的BEM实现、Sass语法、Css的编码规范,不然无从下手。
好吧,我承认了,当第一次看到elementUI的css实现时,震惊了我的狗头
css这块我一直都是很轻视的,但没想到在我眼中不重要的东西,在elementUI这里竟然变的这么精彩

element用到的Sass语法

1、!default!global!default用来定义局部变量,!global可以把局部变量转全局变量

#main {
  $width: 5em !global;
  width: $width;
}
#sidebar {
  width: $width;
}
//编译后
#main {
  width: 5em;
}
#sidebar {
  width: 5em;
}

2、混入:@mixin@include@mixin用来定义代码块、@include引入

@mixin color-links {
    color: blue;
    background-color: red;
}
span {
   @include color-links;
}
// 编译后
span {
  color: blue;
  background-color: red; 
}

3、@content 向混合样式中导入内容
从获取@include{}中传递过来的所有内容导入到指定位置

@mixin apply-to-ie6-only {
  * html {
    @content;
  }
}
@include apply-to-ie6-only {
  #logo {
    background-image: url(/logo.gif);
  }
}
// 编译后
* html #logo {
  background-image: url(/logo.gif);
}

4、@at-root 跳出嵌套
跳出到和父级相同的层级

.block {
    color: red;
    @at-root #{&}__element {
        color: blue;
    }
    @at-root #{&}--modifier {
        color:orange;
    }
}
// 编译后
.block {
   color: red;
}
.block__element {
   color: blue;
}
.block--modifier {
  color: orange;
}

5、@each in 遍历列表

@each $animal in puma, sea-slug, egret, salamander {
  .#{$animal}-icon {
    background-image: url('/images/#{$animal}.png');
  }
}
// 编译后
.puma-icon {
  background-image: url('/images/puma.png'); }
.sea-slug-icon {
  background-image: url('/images/sea-slug.png'); }
.egret-icon {
  background-image: url('/images/egret.png'); }
.salamander-icon {
  background-image: url('/images/salamander.png'); }

6、@if @else 条件判断

p {
  @if 1 + 1 == 2 { border: 1px solid; }
  @if 5 < 3 { border: 2px dotted; }
  @else  { border: 3px double; }
}
// 编译后
p {
  border: 1px solid; 
}

7、sass内置函数
1) map-has-keymap-has-key($map,$key) 函数将返回一个布尔值。当 $map 中有这个 $key,则函数返回 true,否则返回 false。 通过map-get($map,$key)获取$key对应的值

$map: ('xs' : '200px', 'sm': '100px');
$key: 'xs';
@if map-has-key($map, $key) {
  @debug map-get($map, $key) // 200px
}

2) unquoteunquote($string):删除字符串中的引号

@debug unquote('Hello Sass!') // Hello Sass!

3) inspectinspect($value)函数返回 $value 的字符串表示形式

@debug inspect(null); // "null"
@debug inspect(('width': 200px)); // "('width': 200px)"

4) str-indexinspect($str, $value)返回字符串的第一个索引位置(索引从1开始),如果字符串不包含该子字符串,则返回 null

@debug str-index("sans-serif", "ans"); // 2

5) str-slicestr-slice($str, $beginIndex, $endIndex) 截取字符串的指定字符

@debug str-index("(.el-message)", 2, -2); // .el-message

备注:sass中可以使用@debug调试,在控制台打印需要的内容

BEM命名规范

BEM代表 块(block)、元素(element)、修饰符(modifier),三个部分结合使用,生成一套具有唯一性的class命名规范,起到样式隔离,避免css样式污染的作用。
el-input , el-input__inner, el-input--mini打开packages/theme-chalk/src/button.vue
该文件列举了BEM的基础配置,如样式名前缀、元素、修饰符、状态前缀

$namespace: 'el'; // 所有的组件以el开头,如el-input
$element-separator: '__'; // 元素以__分割,如el-input__inner
$modifier-separator: '--'; // 修饰符以--分割,如el-input--mini
$state-prefix: 'is-'; // 状态以is-开头,如is-disabled

element的BEM实现

打开packages/theme-chalk/src/button.vue

定义block

作用:给组件添加el-前缀,通过@contentinclude{}中传递过来的内容导入到指定位置

@mixin b($block) {
  $B: $namespace+'-'+$block !global;  // 使用el-拼接组件名
  .#{$B} {
    @content;
  }
}

组件示例

@include b(button) {
  display: inline-block;
  line-height: 1;
  white-space: nowrap;
}
// 编译后
.el-button {
  display: inline-block;
  line-height: 1;
  white-space: nowrap;
}

定义element

作用:
1、通过__连接符将父级选择器和传入的子元素拼接起来
2、通过hitAllSpecialNestRule函数判断父级选择器($selector: &),是否包含-- .is- 这三种字符
3、如果父级选择器包含这几种字符,输出父级选择器包含子元素的嵌套关系
4、反之原样输出

@mixin e($element) {
  $E: $element !global;
  $selector: &;
  $currentSelector: "";
  @each $unit in $element { // $element传入的值可以单个,也可以是列表
    $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","};
  }

  @if hitAllSpecialNestRule($selector) {
    @at-root {
      #{$selector} {
        #{$currentSelector} {
          @content;
        }
      }
    }
  } @else {
    @at-root {
      #{$currentSelector} {
        @content;
      }
    }
  }
}

组件示例一(父级选择器包含这三种字符)

@include b(message-box) {
    color: blue;
    @include m(center) {
       padding-bottom: 30px;
    @include e(header) {
       padding-top: 30px;
    }
  }
}
//转化为:
.el-message-box {
    color: blue;
}
.el-message-box--center {
    padding-bottom: 30px; 
}
.el-message-box--center .el-message-box__header {
    padding-top: 30px;
}

组件示例二(父级选择器不包含这三种字符)

@include b(message-box) {
    color: blue;
    @include m(header) {
        padding-bottom: 30px;
  }
}
//转化为:
.el-message-box {
    color: blue;
}
.el-message-box--header {
    padding-bottom: 30px; 
}

定义modifier(修饰符)

作用:通过--连接符将父级选择器和传入的修饰符拼接起来

@mixin m($modifier) {
  $selector: &;
  $currentSelector: "";
  @each $unit in $modifier {
    $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
  }

  @at-root {
    #{$currentSelector} {
      @content;
    }
  }
}

组件示例

@include b(button) {
  display: inline-block;
  @include m(primary) {
    color:blue;
  }
}
// 编译后
.el-button {
  display: inline-block;
}
.el-button--primary {
  color:blue;
}

通过element这套BEM规范,使得css编码规范具有很强的可读性和可维护性,也极大精简了css。这一波操作下来,竟然让原来惹人厌的css也变得可爱起来了,爱了爱了

BEM辅助函数

打开packages/theme-chalk/src/mixins/function.scss
补充说明下上文中提到的hitAllSpecialNestRule函数
该函数用来判断父级选择器($selector: &),是否包含-- .is- 这三种字符

/* BEM 辅助函数*/

// 该函数将选择器转化为字符串,并截取指定位置的字符
// 例如(.el-message,) → .el-message
@function selectorToString($selector) {
  $selector: inspect($selector);  // inspect($value)返回$value的字符串表示形式
  $selector: str-slice($selector, 2, -2); // str-slice截取指定字符
  @return $selector;
}

//判断父级选择器是否包含'--'
@function containsModifier($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, $modifier-separator) { // str-index返回字符串的第一个索引
    @return true;
  } @else {
    @return false;
  }
}

//判断 父级选择器是否包含'.is-'
@function containWhenFlag($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, '.' + $state-prefix) {
    @return true
  } @else {
    @return false
  }
}

// 判断父级是否包含 ':' (用于判断伪类和伪元素)
@function containPseudoClass($selector) {
  $selector: selectorToString($selector);

  @if str-index($selector, ':') {
    @return true
  } @else {
    @return false
  }
}

// 判断父级选择器($selector: &),是否包含`--` `.is-`  `:`这三种字符
@function hitAllSpecialNestRule($selector) {
  @return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector);
}

到达第二站

到这里已基本了解elementUI的Css编码规范。可以开始打造一套属于自己的组件样式了
后面,就要深入到具体的组件了,路漫漫其修远
下一篇将讲解 elementUI 源码系列三: Button组件 ,我们继续发车

参考链接

sass官方教程elementUI中用到哪些sass语法从element-ui源码来看BEM实现