小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
背景
传统写 css 类名是这么写:
.container .form .input .warpper .icon {
/*写入你需要的css*/
}
.container .form .text {
/*写入你需要的css*/
}
.page .warpper .layout .content{
/*写入你需要的css*/
}
上面这种存在以下问题:
- 维护css的时候,只看HTML,我们无法知道当前css的作用范围,css的表现不够一目了然
- 如果我要覆盖css的样式,可能我就需要利用css的优先级的规则去覆盖原有的css,这样就会导致css优先级竞争
- css编写的时候复用性不高,当存在多个样式一致的时候,我们可能会选择减少命名空间的方式来提升当前css的作用范围,但是可能会导致css样式冲突的问题
BEM
- B - block,表示1个块
- E - element,表示1个元素
- M - modifier,表示块或元素的修饰符 如下,对于 navbar 组件的划分:
整个navbar组件为1个block,类名定为 .navbar,左右侧、中间标题是 element,类名定为 .navbar__left, .navbar__right, .navbar__title,如果 navbar 可以设置为深色模式,则在 .navbar 上再加个 .navbar--dark,这个类名为 modifier。
BEM 的类名写法有
- .b
- .b__e
- .b__e–m
- .b–m 这四种写法。
BEM写法好处
- class 的样式层级只有1层,方便用高优先级的样式轻松覆盖原来的样式
- 通过块、元素、修饰符去定义一个块,类似于组件的拆解设计,容易知道某个类名是作用域哪个地方
SCSS 实现 BEM 类名自动生成
通过使用 SCSS 的 mixin 语法,可以让其自动生成 BEM 类名。
_config.scss
/**
* SCSS 配置项:命名空间以及BEM
*/
$namespace: 'wd'; // 前缀命名空间可以修改,根据自己需要来
$elementSeparator: '__';
$modifierSeparator: '--';
$state-prefix: 'is-';
_function.scss
/**
* 辅助函数
*/
@import 'config';
/* 转换成字符串 */
@function selectorToString($selector) {
$selector: inspect($selector);
$selector: str-slice($selector, 2, -2);
@return $selector;
}
/* 判断是否存在 Modifier */
@function containsModifier($selector) {
$selector: selectorToString($selector);
@if str-index($selector, $modifierSeparator) {
@return true;
} @else {
@return false;
}
}
/* 判断是否存在伪类 */
@function containsPseudo($selector) {
$selector: selectorToString($selector);
@if str-index($selector, ':') {
@return true;
} @else {
@return false;
}
}
_mixin.scss
/**
* 混合宏
*/
@import 'config';
@import 'function';
/**
* BEM
*/
@mixin b($block) {
$B: $namespace + '-' + $block !global;
.#{$B} {
@content;
}
}
/* 对于伪类,会自动将 e 嵌套在 伪类 底下 */
@mixin e($element...) {
$selector: &;
$selectors: '';
@if containsPseudo($selector) {
@each $item in $element {
$selectors: #{$selectors + '.' + $B + $elementSeparator + $item + ','};
}
@at-root {
#{$selector} {
#{$selectors} {
@content;
}
}
}
} @else {
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ','};
}
@at-root {
#{$selectors} {
@content;
}
}
}
}
@mixin m($modifier...) {
$selectors: '';
@each $item in $modifier {
$selectors: #{$selectors + & + $modifierSeparator + $item + ','};
}
@at-root {
#{$selectors} {
@content;
}
}
}
/* 对于需要需要嵌套在 m 底下的 e,调用这个混合宏,一般在切换整个组件的状态,如切换颜色的时候 */
@mixin me($element...) {
$selector: &;
$selectors: '';
@if containsModifier($selector) {
@each $item in $element {
$selectors: #{$selectors + '.' + $B + $elementSeparator + $item + ','};
}
@at-root {
#{$selector} {
#{$selectors} {
@content;
}
}
}
} @else {
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ','};
}
@at-root {
#{$selectors} {
@content;
}
}
}
}
/* 状态 */
@mixin when($state) {
@at-root {
&.#{$state-prefix + $state} {
@content;
}
}
}
/**
* 常用混合宏
*/
/* 单行超出隐藏 */
@mixin lineEllipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 多行超出隐藏 */
@mixin multiEllipsis($lineNumber: 3) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lineNumber;
overflow: hidden;
}
/* 清除浮动 */
@mixin clearFloat {
&::after {
display: block;
content: '';
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
}
/* 0.5px 边框 */
@mixin halfPixelBorder($direction: 'bottom', $left: 0) {
&::after {
position: absolute;
display: block;
content: '';
width: 100%;
height: 1px;
left: $left;
@if ($direction == 'bottom') {
bottom: 0;
}
@else {
top: 0;
}
transform: scaleY(0.5);
background: $-color-border-light;
}
}
@mixin buttonClear {
outline: none;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
background: transparent;
}
scss import 支持忽略文件名前面的 _ 以及后缀名
使用:
@include b(navbar) {
/* 样式 */
@include m(dark) {
/* 样式 */
/* 该修饰符下元素的样式,不支持再嵌套,只能写类名全名
.wd-navbar__left {}
.wd-navbar__right {}
}
@include e(left) {
/* 样式 */
&::before {
/* 支持直接写伪元素和伪类 */
}
}
@include e(right) {
/* 样式 */
/* 支持另一种修饰写法,生成 .wd-navbar__right.is-icon */
@include when(icon) {
/* 样式 */
}
}
}
上面的代码最后生成css如下:
.wd-navbar {}
.wd-navbar--dark {}
.wd-navbar--dark .wd-navbar__left {}
.wd-navbar--dark .wd-navbar__right {}
.wd-navbar__left {}
.wd-navbar__left::before {}
.wd-navbar__right {}
.wd-navbar__right.is-icon {}
点赞支持、手留余香、与有荣焉,动动你发财的小手哟,感谢各位大佬能留下您的足迹。
往期精彩推荐
Vue 虚拟 DOM 搞不懂?这篇文章帮你彻底搞定虚拟 DOM
Git 相关推荐
面试相关推荐
更多精彩详见:个人主页