前言
对于前端开发者来说,css相关问题是无论如何也绕不过去的。因为样式代码是就近生效原则,即同样的选择器定义的样式,后定义的样式代码会覆盖前面定义的样式代码。
class 做为 css 中最常用的选择器,刚入门的前端开发者会很纠结,到底怎么定义class名称,才能保证名称不重复,与此同时最好 class 名称还要有一定的意义,我们通过 class 名称能够很快定位到功能模块。
我记得我刚刚入门前端时,经常定义一些比较单一的 class 名称,比如 header, body, content等等。又或者我去修改同事开发的模块时,通过class去定位具体的代码时,我都会浪费很多的时间,这种事情特别让人烦躁。那么我们到底该怎么去定义class名称才能让大部分都接受并且易于维护呢?有没有一套规范来约束我们去定义统一风格的 class 名称呢?直到我了解到 BEM 命名规范后,我感觉生活突然光明了。
BEM命名规范
BEM 命名规范是由 Yandex 团队提出来的 css 命名规范,其中 BEM 分别是 Block(块)、Element(元素)、Modifier(修饰符) 的首字母。
BEM 规定 class 名称的定义除了有意义的单词外,只能通过 中划线 ( - )、下双滑线 ( __ )、中双滑线进行连接。举个例子: body-head__menu--actived
这个命名想表达的意思是 内容中标题部分中的菜单被选中时。
下面我简单介绍下这三种连接符代表的含义
-
中划线 -
当定义某个模块,要通过多个单词来表达时,我们就需要通过中划线来进行连接,比如例子中
body-head
这一部分也就是 BEM 中的 Block,定义了一个主要模块。 -
双下划线 __
当我们需要在主要模块中区分不同的小模块时,我们可以将它们定义为 Element,那么Block 与 Element 就是通过双下滑线来连接的。比如例子中
body-head
与menu
之间的连接符。 -
双中滑线 –
我们定义不同的模块也会存在多种状态的情况,这个时候我们需要通过不同的修饰符Modifier去区分。Element 与 Modifier 之间通过 – 进行连接。比如例子中
menu
与actived
。
通过人为遵守 BEM 命名规范,还是不能完全避免 class 类型重复从而导致样式覆盖的问题。就像一个项目组中的两个大兄弟心有灵犀取了同样的类名,这个也是避免不了的事。那么就没有一劳永逸的办法来解决这种问题吗?
目前前端工程化的程度已经很高了,我们可以通过工具手段来彻底解决样式覆盖的问题。
CSS Module是什么
引用官方文档的介绍
A CSS Modules is a CSS file in which all class names and animation names are scoped locally by default.
CSS模块是一个CSS文件,其中所有的类名和动画名默认为局部作用域。
相当于给样式加上了作用域,而不是默认的全局作用域。
前端工程化的思想其实是一切皆模块,但是处理样式文件时,我们通用的做法是直接全部引入。
比如 style.css 文件,我在业务模块引入
import './style.css'
<div className="body"></div>
这样我依然在对应的 DOM 上写 class 名称即可。
但使用 css module 时,我们是将 css 文件也当成一个模块引入,然后在具体的 DOM 上加上具体的 css 属性,相当于给DOM赋予了特定的 class,这个class不会作用到其他的没有引用该样式属性的 DOM 上。
import styles from './style.css';
<div className={styles.body}></div>
我们怎么使用这个特性呢?目前前端编译工具都自带了这个能力,比如webpack
、vite
,只需要我们打开对应的配置即可。
BEM 与 CSS Module 结合
这一部分是我目前项目组的实践结论。既然 class module 可以从技术的层面杜绝样式覆盖的问题,那么我们为什么还要使用 BEM 呢?它们两个能够同时使用吗?当BEM 遇上 CSS Module又会发生什么?
首先我说明一下我们为什么还坚持使用 BEM 规范,因为现在的项目组一般都是多人合作开发,我们也经常会改动别人的模块。作为一个前端,我们一般都是从浏览器上发现问题,然后通过开发者工具定位到对应的DOM,然后通过复制DOM的class名称直接到项目中搜索,从而达到最快速度找到问题代码。
如果我们放弃 BEM,那么通过class名称定位到问题代码可能就困难了。毕竟使用 css module,我们不管怎么定义class名称基本都不会重复,那么又会变成整个项目都是header, body, content
,那可太糟糕了。
因为 BEM 只是一个规范,我们是可以同时使用 BEM 和 css module 的。因为BEM规范下的类名都比较复杂,我们需要通过特别一点的方式来取值。
// style.css
.body-head__menu--actived{
color:red;
}
import styles from './style.css'
<div style={styles['body-head__menu--actived']}>乐闻世界</div>
总结
代码首先是给人看的,其实才是给机器运行的
我们平时写代码的时候,要多考虑如果别人接手你的代码时或者帮你修改问题的时候会是什么心情。就像你总是在骂别人的代码就是垃圾一样,将心比心。