干货BEM 的 CSS 编写策略
简短的概括:
1、BEM简介
2、为什么使用BEM
3、BEM的命名规则
4、BEM解决的问题
5、基于 SCSS 的 BEM 方法的实现(element-ui源码解读)
BEM导图
一、BEM简介
BEM的意思就是块(block)、元素(element)、修饰符(modifier),是由Yandex团队提出的一种前端命名方法论。这种巧妙的命名方法让你的CSS类对其他开发者来说更加透明而且更有意义。BEM命名约定更加严格,而且包含更多的信息,它们用于一个团队开发一个耗时的大项目。
二、为什么使用BEM
1)更语意化,可读性更强
通过双下划线(__), 双横杠(--) 等符号代码维护者可以轻松理解每一部分的意义,更强的可读性往往意味着更低的维护成本。
2)模块化,减少层叠带来的样式覆盖的问题
Block是完全独立的存在,其内部的element/modifier的样式都在这个block的命名空间下书写的,所以不会收到其他外部样式的影响,不存在样式覆盖的问题。
3)增强样式的重用性
就像js组合不同的组件得到更复杂的组件一样,我们也可以通过组合不同的block得到更复杂的样式,例如使用.b-btn, .b-input来组合一个简单的form样式,从而提高代码的可复用性,从另一方面讲也是降低了维护成本。
4)更容易做项目迁移
因为block样式是相对独立的,如果在其他项目有需要,我们完全可以讲某个单独的block相关的样式应用到其他项目中。
三、BEM的命名规则
命名约定的模式如下:
.block{}
.block__element{}
.block--modifier{}
说明:
block 代表了更高级别的抽象或组件。
block__element 代表.block的后代,用于形成一个完整的.block的整体。
block--modifier 代表.block的不同状态或不同版本。
之所以使用两个连字符和下划线而不是一个,是为了让你自己的块可以用单个连字符来界定,如:
.site-search{} /* 块 */
.site-search__field{} /* 元素 */
.site-search--full{} /* 修饰符 */
* 举个栗子 ????
1)常规的css
<div class="person">
<div class="female">
<p class="color"></p>
</div>
<div class="male">
<p class="color"></p>
</div>
</div>
这些CSS类名真是太不精确了,并不能告诉我们足够的信息。尽管我们可以用它们来完成工作,但它们确实非常含糊不清。
2)BEM命名规范
<div class="person">
<div class="person_female">
<p class="person_female--color"></p>
</div>
<div class="person_male">
<p class="person_male--color"></p>
</div>
</div>
四、BEM解决的问题
css的样式应用是全局性的,没有作用域可言。考虑以下场景:
**场景一:**开发一个弹窗组件,在现有页面中测试都没问题,一段时间后,新需求新页面,该页面一打开这个弹窗组件,页面中样式都变样了,一查问题,原来是弹窗组件和该页面的样式相互覆盖了,接下来就是修改覆盖样式的选择器...
**场景二:**承接上文,由于页面和弹窗样式冲突了,所以把页面的冲突样式的选择器加上一些结构逻辑,比如子选择器、标签选择器,借此让选择器独一无二。一段时间后,新同事接手跟进需求,对样式进行修改,由于选择器是一连串的结构逻辑,看不过来,嫌麻烦,就干脆在样式文件最后用另一套选择器,加上了覆盖样式...接下来又有新的需求...最后的结果,一个元素对应多套样式,遍布整个样式文件...
BEM解决问题的思路
由于项目开发中,每个组件都是唯一无二的,其名字也是独一无二的,组件内部元素的名字都加上组件名,并用元素的名字作为选择器,自然组件内的样式就不会与组件外的样式冲突了。
BEM的命名规矩:block-name__element-name--modifier-name,也就是模块名 + 元素名 + 修饰器名。
五、element-ui源码解读 BEM 方法
首先来看一个bem命名示例:
.el-message-box{}
.el-message-box__header{}
.el-message-box__header--active{}
如果使用已经封装好的bem方法的话,那么可以写成:
@include b('message-box') {
@include e('header') {
@include m('active');
}
}
接下来我们来看一下bem方法是如何实现的。
bem方法解析
element/packages/theme-chalk/src/mixins/config.scss 里面定义了如下几个变量
$namespace: 'el';
$element-separator: '__';
$modifier-separator: '--';
$state-prefix: 'is-';
然后我们再找到 element/packages/theme-chalk/src/mixins/mixins.scss 文件,找到b,e,m方法。
/* BEM
-------------------------- */
@mixin b($block) {
$B: $namespace+'-'+$block !global;
.#{$B} {
@content;
}
}
@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;
}
}
}
}
@mixin m($modifier) {
$selector: &;
$currentSelector: "";
@each $unit in $modifier {
$currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
**说明:**代码量不多,逻辑也不复杂,语法有点晦涩难懂,解释如下:
-
!global 变量提升,将局部变量提升为全局变量,在其他函数体内也能访问到此变量。
-
@at-root将父级选择器直接暴力的改成根选择器。
-
#{}插值,可以通过 #{} 插值语法在选择器和属性名中使用 SassScript 变量。
总结:
BEM 最难的部分之一是明确作用域是从哪开始和到哪结束的,以及什么时候使用或不使用它。随着不断使用的经验积累,你慢慢就会知道怎么用,这些问题也不再是问题。技术无好坏,合适方最好。
推荐阅读
-
干货BEM 的 CSS 编写策略
-
用 HTML、CSS 和 CV 编写一个动态的小火箭,就大功告成了!
-
用 HTML 和 css 编写的小米商城界面
-
提升网页速度:HTML、CSS与JS加载顺序的优化策略
-
Webpack的三种常用资源指纹策略:js、css与图片、字体文件的个性化设定指南
-
CSS编写指南:统一的编码风格与实践
-
Tailwind CSS - 无需离开HTML即可快速构建美观的网站-引言 在Web开发领域,CSS框架是提高开发效率和维护性的关键工具之一。Tailwind CSS 是一个备受欢迎的、基于原子类的CSS框架,它以独特的方式重新定义了样式表的编写方式。本文将深入介绍Tailwind CSS,探讨它的核心概念、优势以及如何在项目中使用。 1. 什么是Tailwind CSS?