和BEM的战役:10个常见问题及怎样防止

不管你是方才发明BEM或许已是其中熟手(作为web术语来讲),你能够已意想到它是一种有效的要领。假如你还不晓得BEM是什么,我发起你在继承浏览这篇文章之前往BEM website相识一下它,因为我会假定你对这类CSS的要领有一个基本的明白。

本文旨在对那些已是BEM的爱好者或是想要去更有效力的运用它或是异常猎奇而且想去进修它的人有所协助。

如今,我对BEM是一个文雅的定名体式格局已不报有任何空想。它完整不是。我曾很长一段时间摒弃接收它的缘由之一就是它的语法看起来异常貌寝。我心中的设想因子不愿望我文雅的html组织被貌寝的双下划线和连字符弄得一团糟。

而我心中的开发者因子让我务实地对待它。终究,这类用来构建用户界面而且有逻辑性的、模块化的体式格局战胜了我右半边大脑的埋怨:“然则它不够美丽!”我固然不会发起你在像起居室如许小的范围内运用这类体式格局,然则当你须要一件浮水衣(就像你游览在CSS的大海中),我会挑选有用而不是情势。话题拓展的差不多了,以下是10种我已碰到过的逆境和一些怎样处置惩罚它们的技能。

1. 怎样运用子代以至更深条理的挑选器?

首先来诠释这个题目,当你须要挑选一个嵌套凌驾两层的元素,你就会须要用到子孙挑选器。这些挑选器几乎就是我的梦魇,而且我很肯定他们的滥用是人们对BEM发生讨厌的缘由之一,看下面这个例子:

<div class="c-card">

    <div class="c-card__header">
        <!-- Here comes the grandchild… -->
        <h2 class="c-card__header__title">Title text here</h2>
    </div>

    <div class="c-card__body">

        <img class="c-card__body__img" src="some-img.png" alt="description">
        <p class="c-card__body__text">Lorem ipsum dolor sit amet, consectetur</p>
        <p class="c-card__body__text">Adipiscing elit.
            <a href="/somelink.html" class="c-card__body__text__link">Pellentesque amet</a>
        </p>

    </div>
</div> 

就像你想的那样,以这类体式格局定名会很快就会离开掌握,而且一个组件嵌套的越深,越貌寝也越不可读的类名就会涌现。我已运用了一个短块称号c-card和短元素名,比方:bodytextlink,然则你能够设想当块和元素的初始部份被定名为c-drop-down-menu会有何等失控。

我以为双下划线在挑选器称号中只应当涌现一次。BEM代表的是Block__Element--Modifier,而不是Block__Element__Element--Modifier。所以,防止多个元素级的定名。假如存在多级嵌套,你能够就须要从新检察一下你的组件组织。

BEM定名和DOM没有很严厉的联络,所以不管子元素的嵌套水平有多深都没有关联。定名商定只是用来协助你辨认子元素和顶层组件块的关联,在这里就是c-card

这是我对雷同card组件的处置惩罚:

<div class="c-card">
    <div class="c-card__header">
        <h2 class="c-card__title">Title text here</h2>
    </div>

    <div class="c-card__body">

        <img class="c-card__img" src="some-img.png" alt="description">
        <p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p>
        <p class="c-card__text">Adipiscing elit.
            <a href="/somelink.html" class="c-card__link">Pellentesque amet</a>
        </p>

    </div>
</div> 

这意味着一切的子元素都仅仅会被card块影响。所以,我们能够将文本和图片移动到c-card__header,以至在不损坏语义组织的状况下增加一个c-card__footer元素。

2. 我应当运用定名空间吗?

如今,你能够已注重到我的代码示例中运用了c-。这代表“组件”和形成了我定名BEM类名的范例。这个主意来自于致力于提拔代码可读性的Harry Robert’snamespacing technique

这是我采纳的范例,许多前缀会贯串这篇文章的代码示例。

TYPEPREFIXEXAMPLES
Componentc- c-card c-checklist
Layout modulel- l-grid l-container
Helpersh- h-show h-hide
States is- has- is-visible has-loaded
JavaScript hooksjs-js-tab-switcher

我发明运用这些定名空间会使我的代码异常具有可读性。纵然我不能强求你运用BEM,这也相对是一个值得你运用的症结点。

你能够采纳许多别的的定名空间,像qa-能够用作质量保证测试,ss-用作服务器端的钩子,等等。然则上面的列表是一个好的最先,当你以为这项手艺还不错,你能够把它引见给其他人。

在下个题目中,会有一个比较有用的关于款式定名空间的示例。

3. 我该怎样定名包裹容器?

一些组建须要一个掌控子元素规划的容器。在这类状况下,我一般会尝试把规划笼统到一个规划模块中,比方l-grid,而且将每一个组件作为l-grid__item的内容插进去。
在我们card的示例中,假如我们想要去天生具有四个c-card的列表,我会运用下面的html组织:

<ul class="l-grid">
    <li class="l-grid__item">
        <div class="c-card">
            <div class="c-card__header">
                […]
            </div>
            <div class="c-card__body">
                […]
            </div>
        </div>
    </li>
    <li class="l-grid__item">
        <div class="c-card">
            <div class="c-card__header">
                […]
            </div>
            <div class="c-card__body">
                […]
            </div>
        </div>
    </li>
    <li class="l-grid__item">
        <div class="c-card">
            <div class="c-card__header">
                […]
            </div>
            <div class="c-card__body">
                […]
            </div>
        </div>
    </li>
    <li class="l-grid__item">
        <div class="c-card">
            <div class="c-card__header">
                […]
            </div>
            <div class="c-card__body">
                […]
            </div>
        </div>
    </li>
</ul> 

你如今应当明白规划模块和组件的定名空间是怎样一同事情的。

不要畏惧运用一些分外的标记会异常使人头痛。没有人会拍拍你的背然后通知你去把<div>标签移撤除的。

在一些情形下,规划模块是不能够的完整满足要求的。比方说你的网格没有能给你想要的结果,或许你只是想要去语义化的定名一个父元素,你应当怎样做?在差异的场景我偏向去挑选contaniner或许list。照样我们card的例子,我能够会用<div class="l-cards-container">[…]</div> or <ul class="l-cards-list">[…]</ul>或许是<ul class="l-cards-list">[…]</ul>,这取决于运用的前提。症结是要和你的定名商定对峙一致。

4. 跨组件的组建?

我们面对的另一个罕见的题目是组件的款式和位置会遭到父级容器的影响。就这个题目Simurai有许多细致的处置惩罚方法。我这里说一个拓展性最好的体式格局。

假定我们想要在之前的示例的card__body中到场一个c-button。这个按钮自身已是一个组件而且组织以下:

`<button class="c-button c-button--primary">Click me!</button>` 

假如和通例的按钮组件没有款式差异,那末就没有题目。我们只需像下面如许直接运用:

<div class="c-card">
    <div class="c-card__header">
        <h2 class="c-card__title">Title text here</h3>
    </div>

    <div class="c-card__body">

        <img class="c-card__img" src="some-img.png">
        <p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p>
        <p class="c-card__text">Adipiscing elit. Pellentesque.</p>

        <!-- Our nested button component -->
        <button class="c-button c-button--primary">Click me!</button>

    </div>
</div> 

举个例子,假如我们想要让按钮变小一点而且完整是圆角,而这些款式只是c-card组件的一部份。也就是说,当它有一些细小的差异时我们应当怎样办?

之前我说过,我找到一个最好用的跨组件类名的处置惩罚体式格局。

<div class="c-card">
    <div class="c-card__header">
        <h2 class="c-card__title">Title text here</h3>
    </div>

    <div class="c-card__body">

        <img class="c-card__img" src="some-img.png">
        <p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p>
        <p class="c-card__text">Adipiscing elit. Pellentesque.</p>

        <!-- My *old* cross-component approach -->
        <button class="c-button c-card__c-button">Click me!</button>

    </div>
</div> 

这就是BEM官网上有名的“mix”。然则,参考了一些从Esteban Lussich的评价以后,我转变了对这类体式格局的看法。

在上面的例子中,c-card__c-button类尝试去转变一个或多个c-button中存在的属性,然则胜利运用这些款式取决于他们的源递次(或许特别的指定)。c-card__c-button类只会当它在源代码里声明在c-button类以后才会见效。在你构建更多跨组件的组件时会很快失控。(固然,运用!important也是一种挑选,然则我不发起你如许做)

一个真正模块化的UI元素的父元素应当是完整不可知的-不管你在那边运用它,结果都应当是一致的。像“mix”体式格局那样为另一个组件增加具有特定款式的类名,违反了组件驱动设想的开/关准绳,即款式在各模块之间不该当有依靠关联。

最好的方法就是在这些细小的款式差异中运用同一个类,因为你会发明跟着项目的增进你会在别的处所对它举行复用。

`<button class="c-button c-button--rounded c-button--small">Click me!</button>`

纵然你不会再运用这些类,为了运用这些修正,最少也不能把他们和父容器、特别属性和源递次耦合在一同。

固然,另一个挑选就是回到你的设想师岗亭,通知他们这个按钮应当和网站上的其他按钮对峙一致,如许就能够完整防止这个题目。但这是另一码事了。

5. 润饰器照样新组件?

决议组件的起止是一个大题目。在c-card这个示例里,你能够以后会建立另一个叫c-panel的组件,他们两个款式相仿,只要一些纤细的差异。

然则是什么决议他们应当是两个组件呢?c-panelc-card这个块名,或许仅仅是因为一个润饰器在c-card里运用了特别的款式。

如许很轻易过分模块化而且让一切都变成一个组件。我发起从润饰器最先,然则假如你发明你特定组件的CSS文件正变得很难保护,这时刻就能够停止运用润饰器。当你发明你为了顺应你新的润饰器而不能不去重置这个“块”一切的CSS时,就是须要新组件的好时机-最少对我来讲是如许的。

假如你和别的开发者或许设想师合作,最好的体式格局是去讯问他们的看法而且花几分钟去议论。我晓得如许能够有点逃避责任,然则关于一个大型项目来讲,明白哪些模块是可复用的而且在组件的组成上杀青一致是至关重要的。

6. 怎样处置惩罚状况?

这是一个罕见的题目,特别是当你给一个活泼状况的组件编写款式的时刻。让我们假定cards有一个活泼状况,当我们点击它时,它们会被增加上一个悦目的边框。你会怎样去定名这个类?

在我看来你有两种挑选:自力的状况钩或许是一个在组件级相似BEM体式格局定名的润饰器。

<!-- 自力状况勾 -->
<div class="c-card is-active">
    […]
</div>

<!-- BEM润饰器 -->
<div class="c-card c-card--is-active">
    […]
</div> 

只管我更偏向于对峙一致性的相似BEM的定名体式格局,自力类名的优点是运用JavaScript来在恣意一个组件中运用平常的状况钩更轻易。当你不能不运用剧本去运用特定的基于润饰器的状况类时,类BEM的体式格局就很让人头疼了。这固然是完整可行的,然则意味着你须要为每种能够性去编写更多的JavaScript代码。

对峙运用一系列规范的状况钩是有意义的。Chris Pearece有一个编译好的列表,我引荐你去相识一下。

7. 什么时刻能够不在元素上增加类?

我能够明白许多人在须要构建一个庞杂UI的时刻面对的痛楚,特别是他们不习惯去在每一个标签上增加一个类。

一般,我会在须要特别款式的部份上下文增加类名。我会把p标签级的舍弃,除非在这个组件中有特别的需求。

能够预感,这意味着你的html中会包含异常多类名。终究,你的组件能够自力运转而且在没有副作用的前提下在任何处所运用。

因为CSS的全局特征,在一切部份都增加类让我们能够完整掌握我们组件的衬着。最初的心机不适在一全部模块化的体系完成后是完整值得的。

8. 怎样嵌套组件?

假定我们想要在c-card组件中展现一个选项列表,下面这是一个反面教材:

<div class="c-card">
    <div class="c-card__header">
        <h2 class="c-card__title">Title text here</h3>
    </div>

    <div class="c-card__body">

        <p>I would like to buy:</p>

        <!-- Uh oh! A nested component -->
        <ul class="c-card__checklist">
            <li class="c-card__checklist__item">
                <input id="option_1" type="checkbox" name="checkbox" class="c-card__checklist__input">
                <label for="option_1" class="c-card__checklist__label">Apples</label>
            </li>
            <li class="c-card__checklist__item">
                <input id="option_2" type="checkbox" name="checkbox" class="c-card__checklist__input">
                <label for="option_2" class="c-card__checklist__label">Pears</label>
            </li>
        </ul>

    </div>
    <!-- .c-card__body -->
</div>
<!-- .c-card --> 

这里有许多题目。第一个是我们在第一点里提到的子孙挑选器。第二点是一切运用c-card__checklist__item款式都被限制运用,不能复用。

我更偏向于这里须要突破在这个规划模块中的列表自身,而是应当把选项列表零丁笼统成一个组件,如许就能够在别的处所自力运用它们。这里我们运用l-定名空间。

<div class="c-card">
    <div class="c-card__header">
        <h2 class="c-card__title">Title text here</h3>
    </div>

    <div class="c-card__body"><div class="c-card__body">

        <p>I would like to buy:</p>

        <!-- Much nicer - a layout module -->
        <ul class="l-list">
            <li class="l-list__item">

                <!-- A reusable nested component -->
                <div class="c-checkbox">
                    <input id="option_1" type="checkbox" name="checkbox" class="c-checkbox__input">
                    <label for="option_1" class="c-checkbox__label">Apples</label>
                </div>

            </li>
            <li class="l-list__item">

                <div class="c-checkbox">
                    <input id="option_2" type="checkbox" name="checkbox" class="c-checkbox__input">
                    <label for="option_2" class="c-checkbox__label">Pears</label>
                </div>

            </li>
        </ul>
        <!-- .l-list -->

    </div>
    <!-- .c-card__body -->
</div>
<!-- .c-card --> 

如许你就不必反复哪些款式,同时也意味着我们能够在项目中的别的处所运用l-listc-checkbox。能够这意味着更多的标记,然则关于可读性,封装性和可复用性来讲价值能够疏忽。你能够已注重到这些是配合的主题!

9. 组件会不会终究有没有数个类名?

有些人以为每一个元素有大批类名是不好的,--modifiers会越积越多。就我个人而言,我不以为这是个题目,因为这意味着代码更具有可读性,我也能更清楚的晓得它是用来完成什么的。

举个例子,这是一个具有四个类的按钮:

`<button class="c-button c-button--primary c-button--huge  is-active">Click me!</button>` 

我第一眼看到的时刻以为语法不是最简约的,然则异常清楚。

假如这让你异常头痛,你能够检察Sergey Zarouski提出的拓展手艺,我们能够在款式表中运用.className [class^="className"][class*=" className"]来效仿vanilla CSS的拓展功用。假如语法看起来很眼熟,能够是因为和Icomoon处置惩罚它的icon挑选器的体式格局异常相似。

运用这类手艺,你的代码能够会看起来像下面如许:

`<button class="c-button--primary-huge  is-active">Click me!</button>` 

我不晓得运用class^=class*=挑选器是不是比自力的类名表现更好,然则理论上来讲这是一个不错的挑选。我喜好运用复合类名,但我以为这值得那些偏向于寻觅替代品的人注重一下。

10. 我们能够相应式的转变组件的款式吗?

这是Arie Thulank给我提出的题目,我花费了许多心机去想出一个100%详细详细的处置惩罚方法。

一个例子就是下拉菜单在给定断点处转换为选项卡或许是隐式导航在给定断点处转换为菜单栏。实质上是一个组件在媒体查询的掌握下有两种差异的款式表现。

我偏向于给这两个例子去构建一个c-navigation组件,因为他们在两个断点处的行动实质是雷同的。但这让我堕入寻思,假如是图片列表在大屏幕上转化为轮播图呢?这对我来讲是一个边沿状况,只需它有可行的文档及批评,我以为这是合理的。但是运用明白的定名(像c-image-list-to-carousel)来为这类范例的UI组织一次性的自力组件。

Harry Roberts写过一篇相应式后缀来处置惩罚这个题目。他的做法是为了顺应更多规划和款式的变化,而不是全部组件的变化。但我不明白为何这项手艺不能被运用在这里。所以,你会发明作者写的款式像下面如许:

`<ul class="c-image-list@small-screen c-carousel@large-screen">` 

关于差异的屏幕尺寸,这些类就会保存各自的媒体查询。提醒:在CSS中你须要在@前加上\来举行转义,像下面如许:

.c-image-list\@small-screen {
    /* styles here */
} 

我没有太多组织这类组件的示例,然则假如你须要组织这类组件,这将是一个对开发者异常友爱的体式格局。下一个到场的人应当能够轻松明白你的主意。我不首倡你运用像small-screenlarge-screen如许的定名,他们只是纯真为了可读性。

总结

BEM在我建立一个模块化和组件驱动的运用时帮了大忙。我已运用它大概有三年了,上面的这些题目是我在探究时碰到的障碍。我愿望你以为这篇文章是有效的。假如你还没有想要体验BEM,我异常勉励你去尝试一下。

本文依据@David Berner的《Battling BEM (Extended Edition): 10 Common Problems And How To Avoid Them》所译,全部译文带有我本身的明白与头脑,假如译得不好或有不对的地方还请多多指导。如需转载此译文,需说明英文出处:https://www.smashingmagazine….

    原文作者:_Icarus
    原文地址: https://segmentfault.com/a/1190000006135647
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞