一.BEM 简介
BEM はフロントエンド開発方法論(Frontend Development Methodology)を自称し、命名規則、CSS/JS モジュール化原則、ビルドツールを含む開発环节 用の一連の方法を提供しています
P.S. 開発环节を強調するのは Yeoman などのものと区別するためで、BEM が言いたいのは方法であってツールではありません
一連の命名規則(BEM 命名規則)を提供し、主にモジュール化 CSS に使用されますが、BEM では JS やファイル命名など各方面でも使用されています
CSS では主に以下の問題を解決しました:
- チーム協力中のスタイル命名(例えば class)の衝突
長い class 名で解決し、構造化セレクタ(子セレクタ、子孫セレクタなど)を使用せず、クラス名自体に階層関係を持たせます
- コード自体が語る(self-documenting code)という目標の実現
意味論的クラス名で、要素名、機能、所属コンポーネント名などのより多くの情報を提供します
- コンポーネント間の相互影響を回避
構造化セレクタに依存せず、すべてクラス名に頼ります
- 同一 DOM ノード上で各種特性を组装しながら、重複コード(コピペ)を回避する方法
Mix、例えば div.classA>div.classB で Block A と Block B を組合せます
これらの解決策は [如何写好 CSS](/articles/如何写好 css/) で説明されています。長いクラス名形式のセットを定義しただけなら、確かに意味はありません。そのため、BEM はこの命名規則のためにフレームワークとツールも実装し、開発過程で使用できるようにしています
二.用語概念
(スキップして赤い部分を見ることを推奨します。公式説明は説明していないのと同じです)
###Block
ロジックとページ機能の両方が独立したページコンポーネントで、再利用可能なユニットです。特徴は以下の通り:
-
自由にネストして組合せ可能
-
任意のページの任意の位置に配置可能で、機能や外観に影響しません
-
再利用可能で、インターフェースに同一 Block のインスタンスを任意に複数持てます
###Element
Block の構成部分で、Block に依存して存在します(Block を離れると使用できません)
###Modifier
[オプション] Block と Element の外観および動作を定義し、HTML 属性のように、同一種類の Block を異なる見た目にするできます
###BEM entity
上記 3 つすべて
###Mix
単一 DOM ノード上で各 BEM entity が構成する 1 つのインスタンスで、特徴は以下の通り:
-
数個の BEM entity の動作、スタイルを結合でき、コードの重複を回避
-
既存の BEM entity に基づいて意味論的に新しいインターフェースコンポーネントを作成
###BEM tree
BEM を使用して web ページ構造を記述
###Block implementation
多くの異なる技術が BEM entity の動作、外観、テスト、テンプレート、ドキュメント、依存関係記述、追加データ(画像など)などの側面を決定
###Implementation technology
1 つの Block を実装するために使用する技術で、1 種または複数種可能
###Block redefinition
異なるレベルで Block に新特性を追加して Block の実装を修正
###Redefinition level
一連の BEM entity およびその一部の実装
使用中はロジックレベルと見なせます。例えば project level、library level で、前者は後者の Block の機能および外観を再定義できます
P.S.実際 はファイルディレクトリと順序付き導入を通じて実現するレベル隔離と再定義です
注目が必要 な点:
-
B(Block):モジュールを表し、最小の再利用可能ユニットで、機能が独立し、ネスト、組合せ使用可能
-
E(Element):B の構成部分
-
M(Modifier):E の状態 を表し(異なる状態の E は異なる機能と外観を持つ)、B の構成部分でもある
-
BEM tree:BEM 用語でページ/プロジェクト構造を記述
-
Block implementation:Block 実装に必要なもので、JS、CSS、image などすべての関連内容を含む
-
Redefinition level:ロジックレベルと見なせ、高レベルで低レベルの Block(モジュール)を再定義/拡張可能
三.BEM 命名規則
block-name__element-name_mod-name、例えば nav-menu__item_active
block-name 自体はスタイルに対応せず、Block のロジック名としてのみ機能
BEM は一般的方法を提供するだけで、この命名規則を使用しなければならないと限定しているわけではありません
四.JavaScript for BEM
###命名規則は JS にも同様に適用
JS では、Modifier は Block または Element のロジックを表現します(CSS では、Modifier は外観を定義)。JS は一連の状態を通じて Block と Element の動作を記述します
class を通じてコンポーネントを検索せず、Block 名を通じて検索します(HTML では、class のみで一意に标识するのではなく、タグ、属性なども可能)。例えば:
document.querySelector('.button')
.addEventListener('click', function() {
document.querySelector('.popup').classList.toggle('popup_visible');
}, false);
// BEM Style(伪代码)
block('button').click(function() {
block('popup').toggleMod('visible');
});
実際の適用には i-bem.js の導入が必要です
Modifier は Block の具体的な状態を設定でき、Modifier の追加と削除を通じて Block の状態切り替えを実現します。Modifier への操作はイベントをトリガーし、関連 Block に通知します。このメカニズムが正しく実行されることを確保するために、状態を随意に切り替えたり、特定の DOM ノードの class を直接修正したりすることは許可されず、これらの操作は BEM が提供する helper を通じて実現する必要があります(この制限は svn ローカルリポジトリに類似)
Modifier は状態 に対応し、状態は機能に関連します。Modifier を追加すると、状態が変化し、自動的に該当機能を適用します。Modifier を削除すると、該当機能が自動的に削除されます
Modifier と状態および底层との関連を定義:
block('button').onSetMod({
focused: {
true: this.onFocus,
'': this.onBlur
}
});
状態の下のものを屏蔽 します(Modifier の追加/削除による状態変化、またはユーザー行動によるトリガーの可能性)。プログラミングレベルで注目する最小ユニットは状態(Modifier)です。Modifier にスタイルを追加して各状態の外観を定義でき、Redefinition level を通じて Block の動作を変更または完全に再定義もできます
###コードを各ファイルに分離し、ファイルディレクトリ構造規則に従う
規則:
-
各 Block のロジックとオプションの Element および Modifier を個別ファイルに配置
-
各コンポーネントの JS ファイルディレクトリ構造は BEM ファイルディレクトリ構造規則に従う
示例:
logo/
logo.css # Block の外観
logo.tmpl # Block の HTML 表現を生成するためのテンプレート
logo.js # ブラウザでの Block の動的動作
管理、再利用、移植、Redefinition level と組合せ使用のサポートに便利
###ロジックを各 Redefinition level に分離
異なる Redefinition level で新 Block を実装し、既存の Block を継承して拡張するか、または該 Block を完全に再定義できます。例えば:
// 完全に再定義
block('button').onSetMod({
focused: {
true: this.someCustomOnFocused
}
});
// 一部再定義
block('button').onSetMod({
focused: {
true: function() {
this.__base.apply(this, arguments);
this.someCustomOnFocused();
}
}
});
###原則
JS 中の Block と DOM ノードには強対応関係がありません。例えば、DOM 表現を持たない Block もあり、単なるロジックブロックです
Block 間の相互作用はイベント購読を通じて実現します。例えば:
-
他の Block インスタンスのイベントを購読
-
Modifier 変更を購読
-
他の Block の公開インターフェースを直接呼び出し
-
Event Channel(メッセージメカニズム)などの他の任意の相互作用モード
Element は所属する Block が提供する API を通じてのみアクセス可能で、特定の Block 中の Element を直接 アクセスすることはできません
五.ファイルディレクトリ構造
コードは独立した小部分に分解され、独立 Block の開発に便利で、发布前に组装して最適化します
###特徴
- Block implementation を各個別ファイルに分解
管理(開発過程)と移植(他のプロジェクトへの植入)に便利
- オプションの Element と Modifier も各個別ファイルに保存
関連する Block implementation のみ導入(オンデマンドロード)
- ファイルをファイルタイプではなく意味論でグループ化
例えば、必要な Block のみ導入を確保するために、すべての Block を block ディレクトリに配置
- プロジェクトを各 Redefinition level に分割
重複コードを排除し、既存 Library Block の再定義/拡張に便利。Library Block が更新されても、上層の再定義/拡張に影響しません
###ディレクトリ構造示例
blocks/
input/ # input block directory
_type/ # type modifier directory
input_type_search.css # Implementation of modifier type
# with value search in CSS technology
__box/ # box element directory
input__box.css
input.css
input.js
button/ # button block directory
button.css
button.js
button.png
popup/ # popup block directory
_target/
popup_target.css # Common code of modifier target
popup_target_anchor.css # Modifier target with value anchor
popup_target_position.css # Modifier target with value position
_visible/
popup_visible.css # Boolean modifier visible
popup.css
popup.js
ファイル名も BEM 命名規則に従い、Modifier 間の共有部分は個別ファイル(popup_target.css)として抽出可能
###Redefinition level 示例
library.blocks/
button/
button.css # CSS implementation in the linked library (height 20px)
project.blocks/
button/
button.css # Redefinition of CSS implementation (height 24px)
ファイルを異なるレベルに配置し、分離して相互影響を回避
またはプラットフォームで階層化:
common.blocks/
button/
button.css # Generic CSS implementation of the button
desktop.blocks/
button/
button.css # Desktop platform-specific button features
mobile.blocks/
button/
button.css # Mobile platform-specific button features
build 過程は自動的にレベル別に導入します。例えば:
@import(common.blocks/button/button.css); /* Generic CSS rules */
@import(desktop.blocks/button/button.css); /* Desktop platform-specific */
六.build
BEM プロジェクトはすべてマルチレベルファイル構造を持ち、build が完了する作業は:
-
Block 関連の個別ファイルを統合
-
オンデマンドで Block、Element、Modifier を導入
-
正しい順序で導入を確保(Redefinition level を保証)
もちろん、事前にいくつかの依存情報を定義する必要があります。例えば:
-
ページ作成時に関連する Block、Element、Modifier をリスト
-
ページ中の Block、Element、Modifier の依存関係を指明
依存関係声明はツールを通じて完了できます。例えば、HTML 中で適用された class に基づいて自動的に BEM Tree を生成、またはプロジェクト構造に基づいて依存情報を生成(オンデマンド生成ではなく、すべてのものがパッケージされ、プロジェクトディレクトリ内のすべてのものが必須であることが確定している場合に適用。例えばクラスライブラリ)。依存関係声明方式の詳細情報については、Declarations in BEM をご覧ください
依存関係導入方案は実装に関係します。例えば、CSS は @import を使用し、JS は AMD などの方案を使用
###build 結果
// build 前
blocks/ # Directory containing the blocks
bundles/ # Directory containing all build results
hello/ # Directory for the hello page
hello.decl.js # List of BEM entities required for the hello page
An example of a post-build file structure of a BEM project:
// build 後
blocks/ # Directory containing the blocks
bundles/ # Directory containing all build results
hello/ # Directory for the hello page
hello.decl.js # List of BEM entities required for the hello page
hello.css # Built CSS file for the hello page
hello.js # Built JavaScript file for thehello page
上例中 hello.decl.js で依存するコンポーネントおよびその依存関係を定義
###build tool
公式が提供する build ツールは ENB で、非常に強力と言われています:
The BEM platform uses ENB, which is a tool suitable for building BEM projects of any level of complexity.
試してみたい場合は初心者チュートリアルをご覧ください:Starting your own BEM project
###参考資料
コメントはまだありません