当前位置: 首页 > Web前端 > CSS

CSS规范--BEM介绍

时间:2023-03-30 15:57:03 CSS

这段时间在整理前端部分的代码规范。上面提到的CSS规范都会涉及到选择器的命名。请参考BEM命名规范。内容整理如下,供大家参考。请正确!有兴趣的可以移步CSS编码规范。BEM是Yandex推出的一套CSS命名规范。官方对它的描述是这样的:BEM是一种可以让你快速开发网站并维护多年的技术。一开始,Yandex推出的BEM包含了规范及其配套的构建工具。今天说的BEM,主要是指规范。在最新的BEM推广页面中是这样描述的:BEM是一种可以帮助你在前端开发中实现组件复用和代码共享的命名方式。BEM解决的问题css的样式应用是全局的,完全没有作用域。考虑以下场景:场景一:开发一个弹窗组件,在现有页面上测试是可以的。一段时间后,需要一个新的页面。当页面打开弹窗组件时,页面的样式发生变化。查看了一下问题,原来是弹窗组件和页面的样式重叠了,接下来就是修改叠加样式的选择器了。。。又过了一段时间开发一个新页面,每次我命名该元素时,我都被吓坏了。写一个样式,按几次F5,测试每个组件...场景二:继上面,因为页面和弹窗样式冲突,所以在页面的冲突样式选择器中添加一些结构逻辑,比如子选择器、标签选择器,使选择器独一无二。过了一段时间,新同事接手跟进需求,修改样式。由于选择器是一系列的结构逻辑,理解起来比较麻烦,所以他干脆在样式文件的末尾使用了另一套选择器,并加了一个override样式。...然后又有了新的需求...最后的结果是一个元素对应多套样式,分布在整个样式文件中...以前开发组件的时候,我们用“概率duplicationissmall”或者干脆”在那个时候保证样式不与“认为唯一的名称”冲突是不可靠的。理想情况下,在开发一组组件的过程中,我们应该能够任意命名元素而不用担心关于是否和组件外的样式冲突,BEM解决这个问题的思路是,在项目开发中,每个组件都是唯一的,它的名字也是唯一的,组件内部元素的名字加上组件name,元素的名字作为选择器,组件内部的样式不会和组件外部的样式冲突,这是通过组件的唯一性来保证选择器的唯一性name,从而保证样式不会被组件外污染。BEM意为块(block)、元素(element)、修饰符(modifier),是Yandex团队提出的一种前端命名方法论。这种巧妙的命名方法使您的CSS类对其他开发人员更加透明和有意义。BEM命名约定更严格并且包含更多信息,它们被团队用于开发大型、耗时的项目。命名约定的模式如下:.block{}.block__element{}.block--修饰符{}.block表示更高级别的抽象或组件。.block__element代表.block的后代,用于整体组成一个完整的.block。.block——修饰符代表.block的不同状态或不同版本。BEM的全部意义在于,名称本身就可以告诉其他开发人员标签的用途。通过浏览HTML代码中的class属性,您可以了解模块是如何关联的:有些只是组件,有些是这些组件的后代或元素,还有一些是组件符号的其他形式或装饰。让我们用一个类比/模型来思考以下元素是如何相关的:.person{}.person__hand{}.person--female{}.person--female__hand{}.person__hand--left{}人,其中有手等元素。一个人也有其他的形式,比如女人,这又是有自己的要素的。下面我们把它们写成“常规”的CSS:失去联系。以.female为例,它是指女性人类还是某种雌性动物?.hand,它是指时钟的指针吗?还是手打牌?使用BEM我们可以获得更多的描述和更清晰的结构,并且我们可以通过在代码中命名来了解元素之间的关系。BEM真的很强大。让我们看一个以“常规”方式命名的.site-search示例:这些CSS类名确实不够精确,并没有告诉我们足够的信息。虽然我们可以用它们完成工作,但它们确实很模糊。使用BEM表示法将如下所示:从CSS的写法我们已经知道.media__img和.media__body必须位于.media内部,而.media__img--rev是.media__img的另一种形式。仅通过CSS选择器的名称,我们就可以获取上述所有信息。BEM的另一个好处是针对以下情况:欢迎来到FooCorpFooCorp是最棒的,真的!

代码,我们根本不明白.media和.alpha这两个类是如何相互关联的?同样,我们也无从知晓.body和.lede之间,或者.img-rev和.media之间是什么关系?从这个HTML中(除非你非常了解媒体对象),我们不知道这个组件是由什么组成的,它还有什么其他形式。如果我们以BEM方式重写这段代码:>欢迎来到FooCorpFooCorp是最棒的,真的!

我们将能够明白.media是一个块,.media__img--rev是.media__img的修改变体,是属于.media的元素。而.media__body是没有变化的元素,属于.media。以上信息都可以通过类名来理解,看来BEM确实很有用。使用BEMFAQ1丑陋!通常人们认为BEM很丑。我敢说,如果你仅仅因为这种代码看起来不太好看而羞于使用这种代码,那你就错过了最重要的东西。除非使用BEM使您的代码不必要地难以维护,或者它实际上使您的代码更难阅读,否则在使用它之前请三思。但是,如果只是“看起来有点怪”,实际上是一种有效的手段,那么在开发之前当然要充分考虑。是的,BEM确实看起来很奇怪,但它的好处远远超过它的外观缺陷。BEM可能看起来有点滑稽,可能会导致我们输入更长的文本(大多数编辑器都有自动完成功能,而gzip压缩会让我们担心文件大小),但它仍然很强大。2、名字是不是太长了?BEM命名包含模块名,长命名会使HTML标签显得臃肿。事实上,每一个使用BEM的开发团队都会或多或少地改变其命名约定,比如Instagram团队使用的驼峰命名法:.blockName-elementName--modifierName{/*...*/}和单下划线:.block-name_element-name--modifierName{/*...*/}还有单横线连接的修饰符名称:.blockName__elementName-modifierName{/*...*/}其实这些对缩短命名,但我们无需担心文件大小。由于服务器有gzip压缩,相同BEM名称的部分很多,压缩后的体积不会太大。另外现在写代码都是用IDE,有自动提示功能,不用担心重复输入太长的名字。由于命名很长,我们可以使用子选择器而不是BEM命名吗?这使得HTML标签看起来更好,至少在编写HTML时是这样。3.什么时候使用BEM?当您确实使用BEM时,请务必记住您不必在任何地方都使用它。例如:.caps{text-transform:uppercase;}这个CSS不属于任何BEM类别,它只是一个单一的样式。另一个不使用BEM的例子是:.site-logo{}这是一个标志,我们可以像这样用BEM格式写它:.header{}.header__logo{}但我们不必这样做。使用BEM的诀窍是知道什么时候应该用BEM格式写什么。仅仅因为某些东西实际上在一个块中并不意味着它是BEM中的一个元素。在此示例中,网站徽标恰好位于.header内,它也可以位于侧边栏或页脚中。元素的作用域可以在任何上下文中开始,因此请确保只在需要的地方使用BEM。再看一个例子:Loremipsumdolor...在这个例子中,我们可能只需要另一个类,它可以调用它。标题;它的样式取决于它的级联方式,因为它在.content中;或者它恰好在.content中。如果是后者(即碰巧在.content中,并非总是如此),我们就不需要使用BEM。然而,一切都有可能使用BEM。让我们再看看.site-logo的例子,假设我们想给我们的网站添加更多的圣诞氛围,所以我们想要一个圣诞版的标志。所以我们有以下代码:.site-logo{}.site-logo--xmas{}我们可以使用--修饰符快速构建我们代码的另一个版本。BEM最难的部分之一是了解作用域的起点和终点,以及何时使用(和不使用)。接触多了,经验积累了,你就会慢慢知道怎么用,这些问题就不再是问题了。4您是否使用了错误的BEM?刚开始了解BEM的时候,可能会有误解,会出现以下不正确的命名方式:首页分页组件有一个ul列表,名字为:page-btn__list,每个页面的按钮都存放在列表中,名字为:page-btn__list__item__link,这是错误的。首先,和BEM的命名规范相反,BEM的命名只包含三部分,元素名只占一部分,所以元素名不能有多个,所以上面每个页面的按钮名可以改成:页btn__btn。相反,它应该如下所示:第一页其次,与BEM思想相反,BEM并不考虑结构是的,比如上面的分页按钮,即使在ul列表中,它的命名也不应该考虑它的父元素。当我们遵循这个规则时,无论是父元素的名称发生变化,还是模块结构发生变化,或者元素之间的层次关系发生变化,这些都不会影响元素的名称。因此,即使需求发生变化,分页组件也应该有按钮,DOM结构发生变化,顶多是不同元素的增删改查,以及模块中名称的增删改查,而不是修改名称,不会因为改名而涉及到JS文件的修改或样式文件的修改。5.关于BEM装饰器BEM装饰器代表元素的状态,但是有时候元素的状态需要通过js来控制。这个时候按照规范是没有好处的,比如激活状态。BEM推荐的写法是:.block__element{display:none;}.block__element--active{display:block;在使用js给元素添加状态的时候,我们需要知道元素的名字block__element,这样我们就可以推导出它的激活状态为block__element--active,这是不合理的,因为很多时候我们无法知道元素的名字元素,所以这个时候,我们应该统一js控件状态的类名格式,比如is-active,js-active等,这些类名只是用来标识,不允许有默认的public样式:.block__element{display:none;}.block__element.is-active{display:block;}6.关于原子类(shortclass)和BEMBEM,没有必要使用原子类,但是如果已经引入对于Bootstrap这样的框架,没必要强行避免使用原子类,比如pull-right、ellipsis、clearfix等,这些类非常实用,可以和BEM互补。其实在组件开发中并不推荐使用原子类,因为它会降低组件的复用性。可重用性的理想状态是组件不仅在不同的页面中表现一致,而且跨项目也能很好地工作。如果组件的样式因为依赖某些原子类而依赖于整个Bootstrap库,那么组件迁移的负担就会重很多。原子类更适合应用在实际页面中,因为页面变化很大,不能复用。假设在header中,我们使用了两个组件logo和user-panel(用户操作面板),这两个组件分别放在header的左右两侧,我们可以这样写:header可以封装成一个模块,但是复用性不高,不能算是一个组件,所以即使你使用atoms类也无所谓。在项目中,在使用原子类之前,要考虑场景是否变化较大,不能复用。如果是这样,我们就可以放心使用原子类了。组件应该是“自洽的”,本身应该构成一个“生态系统”,也就是说,它几乎不需要外部供给,可以自行运行。7.关于子选择器子选择器的方法是通过组件根节点的名字来选择子元素。按照这个思路,分页按钮样式可以这样写:.page-btn{/*...*/}.page-btn.list{/*...*/}HTML看起来好多了,但这是否解决了样式问题冲突问题??试想,如果让你接手这个项目,你需要增加一个需求和一个新的组件,你确定命名吗?你面临的问题是:你打开组件目录,有一个叫page-btn的分页组件,但是你不知道新组件怎么命名,因为即使新组件的模块名和page-btn不一样,不保证新组件不会和分页组件冲突。比如新的需求是“增加一个列表组件”,如果组件的名字是list,它的根节点的名字是list,那么这个组件下写的样式很有可能和.page的样式冲突-btn.list:.list{/*...*/}只有两个组件。在实际项目中,有十几个或几十个组件。我们是否要检查每个组件,看新的组件名称是否与“以前组件的子元素命名冲突”?不现实。BEM禁止使用后代选择器,上面是原因之一。childselector不好的地方就是层级关系太长,逻辑不清晰,不利于维护。为了懒得命名或者追求所谓的“简化”code",写入如下选择器:.page-btnbutton:first-child{}.page-btnullia{}/*...*//*维护代码,新需求*/.page-btn.prev{}使用DOM结构层级来定位元素,可能会因为需求的变化而导致样式文件的大规模重写,试想维护这样的代码是多么的痛苦,我们不得不去检查上下文DOM结构元素,同时与css文件一一对比,找到该元素对应的样式,即也就是说,为了改变一个元素的代码,我需要不断地翻阅HTML文件和CSS文件,可维护性很差。更有什者,前来维护这段代码的同事直接在样式文件末尾加上override样式,会造成一个很严重的问题:同一个元素的样式分散在文件的不同地方,并且元素选择器的位置也可能不同。这样的样式文件只会越来越差。可以说,当我们使用子选择器来定位元素时,这个样式文件注定要被一遍又一遍地重构。甚至,每个维护这个文件的人都会重构一遍。子选择器也会造成重量过大的问题。当我们要响应时,样式元素需要适应不同的屏幕。这时候我们就得不断确认元素前的选择器怎么写!为了覆盖之前的超重样式,甚至可以通过添加额外的类名或标签名来增加权重。可以想象,从那时起,维护这个样式文件的难度就像滚雪球一样,越来越大。如果我们使用的是BEM,覆盖样式非常简单:找到要覆盖样式的元素,知道它的类名,在媒体查询中使用它的类名作为选择器,写覆盖样式,样式就会是成功重写现在,你不用担心前面样式的权重过大了。