平常的工做中,咱們無時無刻不在和樣式打交道。沒有樣式的頁面就如同一部電影,被人隨意地在不一樣地方作了截取。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範式。