作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Tomislav matijevic的头像

Tomislav Matijević

Tomislav是一位精通前端技术的软件工程师, 特别是在JavaScript和React中.js.

Expertise

以前在

Netgen
Share

什么是边界元法?

当你建立较小的网站时,如何组织你的风格通常不是一个大问题. 您创建了常用的文件,编写了所有需要的CSS,这就是全部. 然而,当涉及到更大、更复杂的项目时,如何组织代码就变得非常重要 crucial. 如果您在一个由多个前端和后端开发人员组成的团队中工作,那么代码的结构就更加重要了.

BEM方法将极大地提高代码的可维护性并加快开发过程

BEM方法将极大地提高代码的可维护性并加快开发过程

Today, 有很多方法的目的是减少CSS代码,使您的CSS代码更易于维护. 在本文中,我将解释并提供其中一个的几个例子: BEM. BEM代表 Block Element Modifier. 它背后的主要想法是加快开发过程, 并通过将CSS类安排到独立的模块中来简化开发人员的团队工作. 如果你见过这样的类名 header__form——搜索,这就是BEM的实际应用. 是的,类的命名可以很长,但它们都是可读和可理解的.

注意,最佳实践是只对类使用BEM, 而不是id,因为类允许您在必要时重复名称并创建更一致的编码结构. Also, 如果你想把你的网站分成有组织的模块, 它应该由相同的结构组成:block, element, 和修饰符. 每个块可以有多个元素,并且块和元素都可以有多个修饰符. 但是,让我们首先从基本BEM结构开始,并通过示例来解释它.

Block

块表示网站中的一个对象. 把它想象成更大的代码结构块. 如今每个网站上最常见的块是标题、内容、侧边栏、页脚和搜索. BEM中的块总是链接CSS类的起点. 让我们来看几个block的例子:

  • a content
  • a menu
  • 搜索表单
.content{/*样式*/}
.菜单{/*样式*/}
.搜索{/*样式*/}

Element

元素是块中执行特定功能的组件. 它应该只在其块的上下文中有意义:

  • 内容文章
  • 菜单项
  • 搜索输入字段
.content__article{/*样式*/}
.menu__item{/*样式*/}
.search__input{/*样式*/}

Modifier

修饰符是我们如何表示块的变化. 如果你曾经使用过Bootstrap,那么最好的例子就是按钮大小. 按钮大小只是按钮本身的大小变化,这使得它成为修饰符:

  • 内容特色文章
  • 菜单链接
  • 有或没有图标的搜索字段
.content__article——featured {/*样式*/}
.menu__item——link{/*样式*/}
.search__input——icon{/*样式*/}

命名约定

BEM方法的主要目的是使CSS选择器的名称尽可能具有信息性和透明性. 原始BEM样式是这样定义的:

Block 名字通常是一个单字 .header,但如果有较长的块定义,则用单个连字符分隔 -:

.lang-switcher{/*样式*/}

Element 名称以双下划线开头 __:

.lang-switcher__flag{/*样式*/}

Modifier 名称以单个下划线开头 _:

.lang-switcher__flag_basic{/*样式*/}

BEM方法中只有一条非常关键的规则——修饰符不能在其所有者的上下文中使用.

Example:

.btn_big{/*样式*/}

你可以用 btn_big 仅当header也被定义时.

不好的例子:

...

很好的例子:

...

除了这些原始的BEM风格, 还有其他的命名方案,比如Harry Roberts和CamelCase.

哈里·罗伯茨风格的例子:

.block-name__element-name——modifier-name{/*样式*/}

CamelCase风格的例子:

.BlockName__ElementName_ModifierName{/*样式*/}

还有一些其他的,但这两个是最常见的. Personally, 我是哈里斯·罗伯茨提出的命名惯例的粉丝, 它有以下规则:

  • 名字都是小写的
  • BEM实体名称中的单词由连字符分隔 -
  • 元素名与块名之间用双下划线分隔 __
  • 布尔修饰符由双连字符分隔 --
  • 没有使用键值类型修饰符

这种命名约定之所以比其他命名约定好得多,是因为您可以轻松地将修饰符元素与其他元素区分开来. 在最初的命名约定中,修饰符是这样定义的:

.block__element_modifier{/*样式*/}

但是正如您所看到的,单下划线和双下划线之间没有太大的区别. 另一方面,双连字符提供了清晰的分隔,你可以立即看到修饰语:

.block__element——修饰符{/*样式*/}

不同格式BEM示例

请注意,除了CSS, BEM在组织JSON时也非常有用, XML, 树文件, 或者任何支持嵌套的格式. 将BEM方法视为构建UI的好方法.

以BEM格式结构化的HTML

让我们考虑以下以BEM格式结构的HTML:

使用JSON和XML格式也可以实现相同的功能.

XML:


  
  
    
    
  
  

JSON:

{
  布洛克:“头”,
  content: [
    {block: ' logo '},
    {
      布洛克:检索表单,
      content: [
        {block: ' input '},
        {block: ' button '}
      ]
    },
    {block: ' lang-switcher '}
  ]
}

BEM项目的文件系统组织

在BEM中,以正确的方式组织文件至关重要. BEM不仅为您提供了一个很好的CSS类组织,并使它们完全可以理解, 还提供了一个非常可维护的文件结构. 让我们以一个项目为例,在SASS文件中使用BEM文件组织技术:

blocks/
  input/
    __box/
      --big/
        input__box——大.scss
      input__box.scss
  button/
    --big/
      按钮,大.scss

正如你在上面看到的, 只需查看主文件夹中的子文件夹结构即可, 一切都清晰有序. 以这种方式, 谁在你后面工作,或者你在别人后面工作,都没有区别, 因为遵循同样的模式是非常容易的.

将BEM项目划分为多个平台

除了使用BEM方法技术组织文件之外, 你也可以研究更具体的东西. 例如, 如果你正在构建一个web项目,这将是完全响应, 并且客户指定移动设备上的一些块与桌面设备上的完全不同, 最好将BEM文件夹结构划分为不同的平台. 在各种平台上组织按钮的示例:

common.blocks/
  button/
    button.scss
desktop.blocks/
  button/
    buttons.scss
mobile.blocks/
  button/
    button.scss

请注意,如果您希望使用BEM方法组织整个项目,这只是一个示例. 具有BEM结构的文件树不是正确使用BEM所必需的, 您可以在项目的某些部分使用BEM. 到目前为止,我还没有使用这种严格的BEM文件结构组织,其中每个元素和修饰符都创建了自己的文件. Instead, 我只是创建一个文件结构的块,它有一个声明的元素和修饰符.

BEM项目的树形组织

边界元法的实践

既然您现在已经熟悉了命名约定,我将在实践中演示BEM方法. 假设我们有这样的HTML代码:


  $3.99
  Product

使用以下CSS标记:

.btn__price{/*样式*/}

.btn__text{/*样式*/}

.btn——big{/*样式*/}

.btn——primary-color{/*样式*/}

现在,不要被误导. 在我们目前的例子中,我们几乎总是有一个block, element, 和修饰符, 事实并非总是如此.

例如,我们有一个block person. 一个人有腿和手,也可以是女性或男性. 如果我们想要定义一个有右手的男性是这样的:

.person——male——right {/*样式*/}

现在你可以看到边界元的真正含义了. 我们定义了一个人,修饰语是性别. 因为不管一个人是男是女,他都有一只手,而手是一个元素. 同样,每个人都可以有右手或左手这也是一个修饰语.

在另一种情况下,如果我们想定义一般人只有一只手,我们会这样做:

.person__hand{/*样式*/}

大家可以看到, 一旦熟悉了BEM,用它来构建CSS和HTML结构就很容易了.

使用BEM和CSS预处理器

就我个人而言,我无法想象在不使用CSS预处理器的情况下启动任何新项目. 大家都知道, 预处理器是一个伟大的东西,它们为我们提供了很多好处, 最重要的是,它们与BEM方法完美匹配.

在下面的示例中,您可以看到BEM与SASS结合使用的最典型示例:

.person {
  &__hand{/*样式*/}

  &__leg{/*样式*/}

  &--male {
    /*样式*/

    &__hand {
      /*样式*/

      &——left{/*样式*/}

      &——右{/*样式*/}
    }

    &__leg {
      /*样式*/

      &——left{/*样式*/}

      &——右{/*样式*/}
    }
  }

  &——女性{
    /*样式*/

    &__hand {
      /*样式*/

      &——left{/*样式*/}

      &——右{/*样式*/}
    }

    &__leg {
      /*样式*/

      &——left{/*样式*/}

      &——右{/*样式*/}
    }
  }
}

SASS代码将编译成以下CSS:

.person__hand{/*样式*/}

.person__leg{/*样式*/}

.person—male {/*样式*/}

.person—male__hand{/*样式*/}

.person——male__hand——left {/*样式*/}

.person——male——right {/*样式*/}

.person—male__leg{/*样式*/}

.person——male__leg——left {/*样式*/}

.person——male__leg——right {/*样式*/}

.person—female {/*样式*/}

.person——female__hand{/*样式*/}

.person——female__hand——left {/*样式*/}

.person——female__hand——right {/*样式*/}

.person——female__leg{/*样式*/}

.person——female__leg——left {/*样式*/}

.person——female__leg——right {/*样式*/}

如果你想更进一步,你可以为BEM使用一个方便的SASS mixins:

///块元素
/// @param {String} $element -元素名称
@mixin element($element) {
    &__ #{$元素}{
        @content;
    }
}

///块修饰符
/// @param {String} $modifier -修饰符的名称
@mixin modifier($modifier) {
    &——#{$修饰符}{
        @content;
    }
}

你可以这样使用它:

.person {
  @include element('hand') {/*人手*/}

  @include element('leg'){/*人的腿*/}

  @include modifier('male') {
    /*男性*/
    
    @include element('hand') {
      /*人男手*/
      @include modifier('left') {
        /*男性左手*/
      }
      @include modifier('right') {
        /*男性右手*/
      }
    }
  }
}

这将产生以下CSS输出:

.person__hand {
  /*人手*/
}

.person__leg {
  /*人腿*/
}

.人——男性{
  /*男性*/
}

.人——male__hand {
  /*人男手*/
}

.人——male__hand离开{
  /*男性左手*/
}

.人——male__hand对{
  /*男性右手*/
}

我知道您很可能不会有这么长的用例, 但这是一个很好的例子,说明了BEM是如何使用的,以及为什么它如此强大, 无论是在小型还是大型项目中.

开始你的BEM项目

正如在 BEM官方文档,启动自己的新BEM项目的最简单方法是使用现有的 GIT存储库. 只需使用Git clone命令:

$ git clone http://github.com/bem/project-stub.git

接下来,进入新创建的目录并安装所有依赖项:

$ NPM install

将安装所有必需的依赖项:

本的依赖性

使用以下命令构建项目 ENB:

node_modules美元/.bin / enb使

运行服务器模式进行开发:

node_modules美元/.bin / enb服务器

结果,出现以下消息:

服务器从0开始.0.0.0:8080

现在,这意味着服务器已经启动并运行. 您现在可以在此地址查看结果:

http://localhost:8080/desktop.包/索引/索引.html

正如您所看到的,已经创建了许多在内部定义的元素 bemjson 文件,位于这里:

project-stub /桌面.包/索引/索引.bemjson.js

您可以查看并探索生成所有HTML的文件的当前结构, 在本地主机上看到的 index.html file. 我们将修改该文件以获得我们在前一章中解释过的“Person”BEM项目. 您可以删除(或注释)整个代码 index.bemjson.js 文件,并将其替换为以下文件:

module.出口= {
    布洛克:“页面”,
    标题:“Person BEM”,
    Favicon: '/ Favicon.ico',
    head : [
        {元素:'meta',属性:{name: 'description',内容:"}},
        {元素:'meta',属性:{name: 'viewport',内容:'width=device-width, initial-scale=1'}},
        {元素:'css', url: '索引'.min.css' }
    ],
    脚本:[{elem: 'js', url: 'index . '.min.js' }],
    content: [
        {
            布洛克:“人”,
            content: [
                {
                    elem:“男”,
                    content: [
                        {
                            elem:“腿”,
                            mod: {side: 'left'},
                            内容:“男性腿——左”
                        },
                        {
                            elem:“腿”,
                            mod: {side: 'right'},
                            内容:“男性腿——右”
                        },
                        {
                            elem:“手”,
                            mod: {side: 'left'},
                            内容:“男性左手”
                        },
                        {
                            elem:“手”,
                            mod: {side: 'right'},
                            内容:“男性右手”
                        }
                    ]
                },
                {
                    elem:“女性”,
                    content: [
                        {
                            elem:“腿”,
                            mod: {side: 'left'},
                            内容:“女性人腿——左”
                        },
                        {
                            elem:“腿”,
                            mod: {side: 'right'},
                            内容:“女性腿——右”
                        },
                        {
                            elem:“手”,
                            mod: {side: 'left'},
                            内容:“女性左手”
                        },
                        {
                            elem:“手”,
                            mod: {side: 'right'},
                            内容:“女性右手”
                        }
                    ]
                },
            ]
        }
    ]
};

现在,将生成以下HTML:

男性的腿——左边
男性的腿——右
男性的手——左边
男性的手——右
女性的腿——左边
女性的腿——右
女性左手——左
女性的手——右

从上面的代码中可以看到, 在这个场景中使用了默认的BEM编码方案,因为我们只是使用BEM提供给我们的默认设置. 还有很多命令和选项可供您探索和使用, 比如创建新页面, blocks, 或修改BEM HTML. 我就不深究了,大家都可以在 BEM官方文档.

边界元法的优势和关注

优势与关注

Advantages

  • BEM非常适合维护. 有多少次,你不得不在一个大型项目中跟随某人,而你只是太害怕改变任何东西,而不会有未知的东西崩溃? 在使用BEM时,您知道元素的确切用途以及它可能出现在哪个块中.
  • 类名是合乎逻辑和直观的, 团队的每个成员都知道这个元素在网站上的作用. BEM为项目中的每个人提供了一个他们可以共享的声明性语法,这样他们就可以在同一页面上了.
  • BEM消除了嵌套的CSS选择器. 每一个HTML元素都有它自己的CSS类,通过它的名字你就知道它的目的是什么. 一个选择器可以统治所有的选择器.

注意事项和常见错误

  • 不要陷得太深. 主要规则应该是不要使用超过两层的父级和子级.
  • 注意你开始块作用域的位置. 这里一个常见的错误是当开发人员使用block时, 但是他没有意识到,在开发的后期,同一个块将具有主父块,这可能会破坏嵌套规则.
  • Avoid SASS @extend. 引用哈里·罗伯茨的话:

通过不在Sass中将类“捆绑”在一起,您可以在视图中创建更多的组合. HTML的书面记录要好得多,因为您可以看到每个类都作用于一块DOM. 你的CSS保持得更简洁,因为你不需要每次创建一个新的UI时都创建新的占位符类(或者组合它们的manifest类).

结论

当我第一次看到BEM编码方案时,我的第一个想法是:

这些类太长,无法编写和读取.

但是在尝试之后,现在我无法想象不使用它就开始一个新项目. For me, BEM极大地提高了我的代码可维护性, 我可以肯定地说,每个被“扔”到基于bem的项目中的开发人员都将很快赶上整个代码结构.

尽管如此,社交网络上仍有很多关于BEM的讨论. 有人说BEM不好, 想知道为什么他们要写这么长的名字类,而不是使用默认的HTML嵌套元素. Well, 没人说你一定要喜欢BEM, 但事实是,大多数前端开发人员都接受它,并发现它非常有用.

就这一主题咨询作者或专家.
预约电话
Tomislav matijevic的头像
Tomislav Matijević

位于 Osijek,克罗地亚

成员自 2015年2月11日

作者简介

Tomislav是一位精通前端技术的软件工程师, 特别是在JavaScript和React中.js.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

以前在

Netgen

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® community.