浅谈element-ui中的BEM范式实践
About
12
min read
(
10
Words
)
Reads
日常的工作中,我们无时无刻不在和样式打交道。没有样式的页面就如同一部电影,被人随意地在不同地方做了截取。
BEM规范应该是对于我们现在前端组件开发中我觉得是最合适的一套范式了。所以,我在自己的日常工作中也是十分的推崇这样的一套CSS范式。
而自己最近也在看各种ui框架的源码,觉得ele对于这块还是处理的蛮好的,所以拿出来说说。
1. BEM
BEM是什么?
BEM范式我在以前自己的文章中简单的说过,就不再赘述了。
我们来看看饿了么在BEM这块有着怎样的实践。
1 2 3 4 5 6
|
$namespace: 'el'; $element-separator: '__'; $modifier-separator: '--'; $state-prefix: 'is-';
|
element在config.scss里面定义了一些基础的配置项目,主要包括四个部分:
- 整套样式的命名空间,命名空间可以带来不同系统样式的隔离,当然缺点就是我们的样式一定是带有一个namespace的前缀出现
- B和E之间的连接符
- E和M之间的连接符
- 状态的前缀,因为有很多的用户行为而带来的激活这样的效果。在js中我们会说到is和as,一个是类型的判定,一个是类型的模糊,这是多态的特性体现。所以,同理的话,is在css中代表的就是当前元素状态的判定,例如:
is-checked(是否被选中)之类等等
2. “B”的定义
1 2 3 4 5 6 7
| @mixin b($block) { $B: $namespace+'-'+$block !global;
.#{$B} { @content; } }
|
ele通过宏b来实现的BEM中B的定义。
这里通过radio来作为假设。最后我们在b中通过!global提升了一个$B: el-radio。这也是改良后的BEM。通过插值语句#{ }生成了.el-radio,然后通过@content向生成的B中导入内容。
导入的内容就是通过调用宏b生成的所有的样式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
| @include b(radio) { color: $--radio-color; font-weight: $--radio-font-weight; line-height: 1; position: relative; cursor: pointer; display: inline-block; white-space: nowrap; outline: none; font-size: $--font-size-base; @include utils-user-select(none);
@include when(bordered) { padding: $--radio-bordered-padding; border-radius: $--border-radius-base; border: $--border-base; box-sizing: border-box; height: $--radio-bordered-height;
&.is-checked { border-color: $--color-primary; }
&.is-disabled { cursor: not-allowed; border-color: $--border-color-lighter; }
& + .el-radio.is-bordered { margin-left: 10px; } }
@include m(medium) { &.is-bordered { padding: $--radio-bordered-medium-padding; border-radius: $--button-medium-border-radius; height: $--radio-bordered-medium-height; .el-radio__label { font-size: $--button-medium-font-size; } .el-radio__inner { height: $--radio-bordered-medium-input-height; width: $--radio-bordered-medium-input-width; } } } @include m(small) { &.is-bordered { padding: $--radio-bordered-small-padding; border-radius: $--button-small-border-radius; height: $--radio-bordered-small-height; .el-radio__label { font-size: $--button-small-font-size; } .el-radio__inner { height: $--radio-bordered-small-input-height; width: $--radio-bordered-small-input-width; } } } @include m(mini) { &.is-bordered { padding: $--radio-bordered-mini-padding; border-radius: $--button-mini-border-radius; height: $--radio-bordered-mini-height; .el-radio__label { font-size: $--button-mini-font-size; } .el-radio__inner { height: $--radio-bordered-mini-input-height; width: $--radio-bordered-mini-input-width; } } }
& + .el-radio { margin-left: 30px; }
@include e(input) { white-space: nowrap; cursor: pointer; outline: none; display: inline-block; line-height: 1; position: relative; vertical-align: middle;
@include when(disabled) { .el-radio__inner { background-color: $--radio-disabled-input-fill; border-color: $--radio-disabled-input-border-color; cursor: not-allowed;
&::after { cursor: not-allowed; background-color: $--radio-disabled-icon-color; }
& + .el-radio__label { cursor: not-allowed; } } &.is-checked { .el-radio__inner { background-color: $--radio-disabled-checked-input-fill; border-color: $--radio-disabled-checked-input-border-color;
&::after { background-color: $--radio-disabled-checked-icon-color; } } } & + span.el-radio__label { color: $--color-text-placeholder; cursor: not-allowed; } }
@include when(checked) { .el-radio__inner { border-color: $--radio-checked-input-border-color; background: $--radio-checked-icon-color;
&::after { transform: translate(-50%, -50%) scale(1); } }
& + .el-radio__label { color: $--radio-checked-text-color; } }
@include when(focus) { .el-radio__inner { border-color: $--radio-input-border-color-hover; } } }
@include e(inner) { border: $--radio-input-border; border-radius: $--radio-input-border-radius; width: $--radio-input-width; height: $--radio-input-height; background-color: $--radio-input-fill; position: relative; cursor: pointer; display: inline-block; box-sizing: border-box;
&:hover { border-color: $--radio-input-border-color-hover; }
&::after { width: 4px; height: 4px; border-radius: $--radio-input-border-radius; background-color: $--color-white; content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0); transition: transform .15s cubic-bezier(.71,-.46,.88,.6); } }
@include e(original) { opacity: 0; outline: none; position: absolute; z-index: -1; top: 0; left: 0; right: 0; bottom: 0; margin: 0; }
&:focus:not(.is-focus):not(:active) { .el-radio__inner { box-shadow: 0 0 2px 2px $--radio-input-border-color-hover; } }
@include e(label) { font-size: $--radio-font-size; padding-left: 10px; } }
|
3. “E”的定义
完成了B以后,就要处理E了。不过在e中多做了两件事:
- 通过
each完成了”BE”的样式的生成,例如是input,那么$currentSelector就是.el-radio__input
- 通过函数处理
containsModifier($selector)、containWhenFlag($selector)、containPseudoClass($selector) 三种情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @mixin e($element) { $E: $element !global; $selector: &; $currentSelector: ""; @each $unit in $element { $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","}; }
@if hitAllSpecialNestRule($selector) { @at-root { #{$selector} { #{$currentSelector} { @content; } } } } @else { @at-root { #{$currentSelector} { @content; } } } }
|
4. “M”的定义
最后是m的生成,基本上原理都和前面说到的是一样的了。
例如:el-radio--medium,用来描述radio的size属性。那么$currentSelector就是.el-radio--medium。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @mixin m($modifier) { $selector: &; $currentSelector: ""; @each $unit in $modifier { $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","}; }
@at-root { #{$currentSelector} { @content; } } }
|
5. 小结
我在这也就是抛砖引玉的说下,精彩的内容还是要大家自己去源码里看,或者自己去试着写一下那就是最好了。
试着写一个vue或者react的组件用上BEM范式去管理类名,肯定也会和我一样,觉得在基于组件化开发的前端项目中,BEM范式绝对是我们管理css的一把利器。
当然,在以后的文章中我也会来说说OO和SMA范式。