基于组件方式的web开发方法,基本思想是将用户界面分成独立的模块。
Block 是一个逻辑上和功能上独立的页面组件,等同于网页组件中的部件(等同于网页中的组件)。Block 封装了行为(Javascript)、模板、样式(CSS)和其他实现技术。独立状态的 Block 可供复用,并且促进项目开发和维护。
嵌套关系
<!-- 'head' 模块 -->
<header class="header">
<!-- 嵌套 'logo' 模块 -->
<div class="logo"></div>
<!-- 嵌套 'search-form' 模块 -->
<form class="search-form"></form>
<!-- 嵌套 'nav' 模块 -->
<div class="nav"></form>
</header>
特点
Block 可以被嵌套到任何其他 block 里面去。例如,一个头部 block 可以包含一个 logo、一个搜索表单和一个登录 block。
2.随意放置
Block 可以在一个页面内任意移动,也可以在页面之间或项目之间移动。Block 作为独立的实体来实现,这使得在页面上改变 block 的位置 并确保其功能和外观一切正常 成为可能。
3.可复用
一个界面可以包含同一个 block 的几个实例。
元素(Element)是一个模块(block)的组成部分,且不能脱离模块单独地被使用。例如,一个菜单项(a menu item )不会在一个菜单块(a menu block )范围之外使用,因此它是一个元素(element)。
1.嵌套关系
<!--
正确的。完整的元素名的结构符合如下模式:
'block-name__element-name'
-->
<form class="search-form">
<div class="search-form__content">
<input class="search-form__input"/>
<button class="search-form__button"></button>
</div>
<div class="search-form__hot">
<a class="search-form__hot-word"></a>
<a class="search-form__hot-word"></a>
</div>
</form>
<!--
不正确的。完整的元素名的结构不符合如下模式:
'block-name__element-name'
-->
<form class="search-form">
<div class="search-form__content">
<!-- 推荐:'search-form__input' 或者 'search-form__content-input' -->
<input class="search-form__content__input"/>
<!-- 推荐:'search-form__button' 或者 'search-form__content-button' -->
<button class="search-form__content__button"></button>
</div>
</form>
如果在模块名称上定义了命名空间,也要保证元素名称是依赖于模块的(block_elem)。
在 DOM 树中,一个模块可以有元素嵌套结构:
<div class="block">
<div class="block_elem1">
<div class="block_elem2">
<div class="block_elem3"></div>
</div>
</div>
</div>
在 BEM 的方法论中,这样的模块结构通常表示为一个并列的元素列表:
.block {}
.block_elem1 {}
.block_elem2 {}
.block_elem3 {}
你可以在不改变每个单独的元素的情况下改变一个模块的 DOM 结构:
<div class="block">
<div class="block_elem1">
<div class="block_elem1"></div>
</div>
<div class="block_elem3"></div>
</div>
2、组成部分
一个元素总是一个模块的一部分,不可以单独使用
<!-- 正确的。元素都位于 'search-form' 模块内 -->
<!-- 'search-form' 模块 -->
<form class="search-form">
<!-- 在 'search-form' 模块内的 'input' 元素 -->
<input class="search-form__input" />
<!-- 在 'search-form' 模块内的 'button' 元素 -->
<button class="search-form__button"></button>
</form>
<!-- 不正确的。元素位于 'search-form' 模块的上下文之外 -->
<!-- 'search-form' 模块 -->
<form class="search-block">
</form>
<!-- 在 'search-form' 模块内的 'input' 元素 -->
<input class="search-form__input"/>
<!-- 在 'search-form' 模块内的 'button' 元素 -->
<button class="search-form__button"></button>
3、可选性
个元素是一个可选的模块组件。并不是所有的模块都必须有元素。
<!-- 'search-form' 模块 -->
<div class="search-form">
<!-- 'input' 模块 -->
<input class="input"/>
<!-- 'button' 模块 -->
<button class="button"></button>
</div>
①如果这段代码可能被重用,并且它不依赖于页面上的其他组件,那你应该创建一个模块。
②如果这段代码在没有父实体(模块)的情况下不能使用,那你应该创建一个元素。
③为了简化开发,元素应该被分割成一小部分-子元素。在 BEM 方法论中,你不能创建元素的元素,在这种情况下,你需要创建一个服务模块,而不是创建一个元素。
Modifier 是一个 BEM 实体,它定义了一个 block 或 element 的外观和行为。
Modifier 可用也可不用(即不一定要用到 modifier)。
Modifier 本质上与 HTML 的属性很相似。同一个 block 会因为 modifier 的使用而 看起来与之前有所不同。
例如,菜单块(the menu block )的外观可能会因为在它身上用了一个 modifier 而改变。
一种用于定义模块和元素的外观,状态和行为的实体。
block-name–modifier-name
block-name__element-name–modifier-name
<!-- 'search-form' 模块有一个 ‘focused’ 的布尔类型的修饰符 -->
<form class="search-form search-form--focused">
<input class="search-form__input"/>
<!-- 'button' 元素有一个 'disabled' 的布尔类型修饰符 -->
<button class="search-form__button search-form__button--disabled">Search</button>
</form>
2、键-值
一个 islands 设计主题的按钮”:menu--theme-islands
block-name–modifier-name-modifier-value
block-name__element-name_modifier-name_modifier-value
<!-- The `search-form` 模块有值为 'islands' 的 `theme` 修饰 -->
<form class="search-form search-form--theme-islands">
<input class="search-form__input">
<!-- The `button` 元素有值为 'm' 的 `size` 修饰 -->
<button class="search-form__button search-form__button--size-m">Search</button>
</form>
<!-- 你不能同时使用两个具有不同值的的相同修饰符 -->
<form class="search-form
search-form--theme-islands
search-form--theme-lite">
<input class="search-form__input">
<button class="search-form__button
search-form__button--size-s
search-form__button--size-m">
Search
</button>
</form>
一个修饰符不能被单独使用。
<!-- 正确的。'search-form' 模块有值为 'islands' 的 'theme' 修饰符 -->
<form class="search-form search-form--theme-islands">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</form>
<!-- 不正确的。'search-form' 丢失了 -->
<form class="search-form--theme-islands">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</form>
混合模式:一种在单一的 DOM 节点上使用不同 BEM 实体的技术
混合模式允许
<!-- 'header' 模块 -->
<div class="header">
<!--
将 'header' 模块的 'search-form' 元素与 'search-form' 模块混合在一起使用
-->
<div class="search-form header__search-form"></div>
</div>
在这个例子中,我们将 header 模块的 search-form 元素与 search-form 模块的行为和样式结合在一起。这种方式允许我们在 header__search-form 元素上设置额外的形状和定位,而 search-form 模块仍然是通用的。因此,我们可以在任何环境中使用模块,因为模块没有指定任何填充。这正是我们可以独立调用模块的原因。
文件系统:在 BEM 方法论中采用的组件概念同样适用于项目的文件结构中。模块、元素和修饰符的实现可以被分在独立的文件中,这意味着,我们单独地使用它们。
search-form/ # Directory of the search-form
__input/ # Subdirectory of the search-form__input
search-form__input.css # CSS implementation of the
# search-form__input element
search-form__input.js # JavaScript implementation of the
# search-form__input element
__button/ # Subdirectory of the search-form__button element
search-form__button.css
search-form__button.js
_theme/ # Subdirectory of the search-form_theme modifier
search-form_theme_islands.css # CSS implementation of the search-form block
# that has the theme modifier with the value
# islands
search-form_theme_lite.css # CSS implementation of the search-form block
# that has the theme modifier with the value
# lite
search-form.css # CSS implementation of the search-form block
search-form.js # JavaScript implementation of the
# search-form block
Block,element 和 modifier 合起来就被成为 BEM entity。它是一个 既可以用来指代单独的 BEM 实体又可以作为 block、element 和 modifier 的总称的 概念。
Mix 是被托管在(being hosted on)一个单独的 DOM 节点上的 不同 BEM 实体(混合而成)的一个实例。
Mix允许我们
把几个 BEM 实体的功能(behavior)和样式 组合在一起,同时避免重复代码
在现有的 BEM 实体的基础上 创建语义上的新界面组件。让我们想一下这种 mix 情形:把一个 block 与 另一个 block 的一个 element 组合在一起。
我们假设,项目里的链接(links)通过一个链接块(a link block)来实现。我们需要把菜单项(menu items )格式化成链接(links)。这里有几种实现方法:
创建一个 可以把菜单项(item)转变成链接(link)的 modifier。实现这样一个 modifier 即必然牵涉到 复制链接块的功能和样式。这样一来就会导致代码重复。
取一个 把一个通用的链接块(link block )与一个菜单块的一个链接元素(a link element ) 组合在一起的 mix。两个 BEM 实体的混合体(mix)可以让我们不用复制代码,就可以使用链接块的基本链接功能 和 菜单块的 CSS 规则。
BEM tree 是网页结构在 block、element 和 modifier 方面的表示(representation)。这是一个在 DOM 树之上的抽象概念,它描述了 BEM 实体的名称、它们的状态、顺序、嵌套和辅助数据。在现实生活中的项目,BEM tree可以呈现在任何支持树结构的形式(format)中。
一个DOM树
<header class="header">
<img class="logo">
<form class="search-form">
<input type="input">
<button type="button"></button>
</form>
<div class="lang-switcher"></div>
</header>
BEM tree
header
├──logo
└──search-form
├──input
└──button
└──lang-switcher
在 XML 和 BEMJSON 格式中,该 BEM tree 则是这样的
XML
<block:header>
<block:logo/>
<block:search-form>
<block:input/>
<block:button/>
</block:search-form>
<block:lang-switcher/>
</block:header>
BEMJSON
{
block: 'header',
content : [
{ block : 'logo' },
{
block : 'search-form',
content : [
{ block : 'input' },
{ block : 'button' }
]
},
{ block : 'lang-switcher' }
]
}
Block implementation 是指一组各不相同的 技术,这些技术决定着 BEM 实体以下几方面:
Implementation technology 是一种用于实现一个 block 的技术。Block 可以用一种或多种技术来实现,例如:
Block implementation 是指通过在不同的层级上增加新的功能到 block 来修改 block。
Redefinition level 是指一组 BEM 实体和它们的部分实现。
一个 block 的最终实现 可以被分成 不同的重定义层级。每一个新的层级都会扩展或覆盖原始的 block implementation。最终的结果由 来自所有按照预设的连续的顺序排列的重定义层级的独立的 block implementation technologies 组合而成。
任何 BEM 的实现技术都可以被重新定义。
例如,有一个连接到项目的第三方库。这个库包含现成的 block implementation。该项目指定的 block 保存在一个另一个重定义层级。比方说,我们需要修改这个库里的某一个 block 的外观。这并不需要在库的源代码里修改 block 的 CSS 规则 或者 在项目里复制代码。我们只需在项目里为 那一个 block 创建额外的 CSS 规则。在生成过程中,最终实现将会结合库级别的原有规则和项目级别中新的样式规则。