平常的工做中,咱们无时无刻不在和样式打交道。没有样式的页面就如同一部电影,被人随意地在不一样地方作了截取。css
BEM规范应该是对于咱们如今前端组件开发中我以为是最合适的一套范式了。因此,我在本身的平常工做中也是十分的推崇这样的一套CSS范式。html
而本身最近也在看各类ui框架的源码,以为ele对于这块仍是处理的蛮好的,因此拿出来讲说。前端
BEM是什么?vue
BEM范式我在之前本身的文章中简单的说过,就再也不赘述了。react
而咱们来看看饿了么在BEM这块有着怎样的实践。element-ui
//element-ui
//config.scss
$namespace: 'el';
$element-separator: '__';
$modifier-separator: '--';
$state-prefix: 'is-';
element在config.scss里面定义了一些基础的配置项目,主要包括四个部分:框架
1.整套样式的命名空间,命名空间能够带来不用系统样式的隔离,固然缺点就是咱们的样式必定是带有一个namespace的前缀出现ide
2.B和E之间的链接符函数
3.E和M之间的链接符组件化
4.状态的前缀 ,由于有不少的用户行为而带来的激活这样的效果。在js中咱们会说到is和as。一个是类型的断定,一个是类型的模糊,这是多态的特性体现。因此,同理的话,is在css中表明的就是当前元素状态的断定,例如:is-checked(是否被选中)之类等等
@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生成的全部的样式。
下面是全部经过mixin b能够生成的样式
//经过宏b生成的全部样式 @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; } }
完成了B之后的话,就要处理E了。不过在e中多作了两件事
1.经过each完成了"BE"的样式的生成,例如是input,那么$currentSelector就是".el-radio + __ + input"
2.经过函数处理@return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector) 三种状况
@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; } } } }
最后是m的生成,基本上原理都和前面说到的是同样的了。
例如:el-radio--medium。用来描述radio的size属性。那么$currentSelector就是".el-radio + -- + medium"
@mixin m($modifier) { $selector: &; $currentSelector: ""; @each $unit in $modifier { $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","}; } @at-root { #{$currentSelector} { @content; } } }
我在这也就是抛砖引玉的说下,精彩的内容仍是要你们本身去源码里看,或者本身去试着写一下那就是最好了。
试着写一个vue或者react的组件用上BEM范式去管理类名,确定也会和我同样,以为在基于组件化开发的前端项目中,BEM范式绝对是咱们管理css的一把利器。
固然,在之后的文章中我也会来讲说OO和SMA范式。