初心者のためのBEM:なぜBEMが必要なのか

公開: 2022-03-10
簡単な要約↬CSSスタイルの分離は、BEM使用の最も頻繁な開始点です。 しかし、これはBEMが提供できる最小のものです。 BEMは、プロジェクトにシステムアプローチをもたらし、混乱を防ぎます。

BEMを使用すると、コードがスケーラブルで再利用可能になるため、生産性が向上し、チームワークが促進されます。 あなたがチームの唯一のメンバーである場合でも、BEMはあなたに役立ちます。 それにもかかわらず、多くの開発者は、BEMのようなシステムアプローチはプロジェクトに追加の境界を設定し、プロジェクトを過負荷にし、面倒で、遅くすると信じています。

BEMのすべての主要な側面を要約した形で収集します。 この記事は、わずか20分でBEMの基本的な考え方を理解し、システムアプローチがプロジェクトに有害であるという偏見を排除するのに役立ちます。

Big BEMは、方法論テクノロジーライブラリ、およびツールで構成されています。 この記事では、方法論自体について詳しく説明します。これは、膨大な数の開発者の集中的な経験であり、あらゆるプロジェクトに体系的なアプローチをもたらすためです。

BEMの実際の事例をいくつか紹介するために、BEMテクノロジに触れ、ライブラリとツールを完全にスキップします。

理論から実践へ:

  • クラス以外のセレクターを使用しない主な理由
  • BEMの基本
    • ブロックと要素
    • モディファイアとミックス
    • ファイル構造内のブロック
  • 方法論の明白でない利点
  • 実用的なケース:BEMはCSSだけのものではありません
  • BEMはカスタマイズ可能なシステムです

それで、BEMはヒーローですか、それとも悪役ですか? それはあなた次第です! しかし、最初に、記事を読んでください。

バットマンのロゴとしてのBEM
ベンバトマン
ジャンプした後もっと! 以下を読み続けてください↓

クラス以外のセレクターを使用しない主な理由

BEM方法論の基本的なルールの1つは、クラスセレクターのみを使用することです。 このセクションでは、その理由を説明します。

  • IDを使わないのはなぜですか?
  • タグセレクターを使用しないのはなぜですか?
  • ユニバーサルセレクターを使ってみませんか?
  • CSSリセットを使用しないのはなぜですか?
  • ネストされたセレクターを使用しないのはなぜですか?
  • セレクターでタグとクラスを組み合わせてみませんか?
  • 組み合わせたセレクターを使ってみませんか-
  • 属性セレクターを使用しないのはなぜですか?

IDは使用しません(IDセレクター)

IDは、HTML要素の一意の名前を提供します。 名前が一意である場合、インターフェイスで再利用することはできません。 これにより、コードを再利用できなくなります。

よくある誤解

  1. JavaScriptを使用するにはIDが必要です。
    最新のブラウザは、IDまたはクラスのいずれかで動作します。 どのタイプのセレクターも、ブラウザーで同じ速度で処理されます。
  2. IDは<label>タグで使用されます。
    コントロール内に<label>を配置する場合、IDは必要ありません。 <input id="ID"><label for="ID">Text</label>の代わりに、 <label><input type="...">Text</label>を使用してください。

タグセレクターは使用しません

HTMLページのマークアップが不安定です:新しいデザインでは、セクションのネスト、見出しレベル(たとえば、 <h1>から<h3> )を変更したり、 <p>段落を<div>タグに変えたりできます。 これらの変更はいずれも、タグ用に記述されたスタイルを壊します。 デザインが変わらなくても、タグのセットは限られています。 別のプロジェクトで既存のレイアウトを使用するには、同じタグ用に記述されたスタイル間の競合を解決する必要があります。

セマンティックタグの拡張セットも、すべてのレイアウトのニーズを満たすことはできません。

例として、ページヘッダーにロゴが含まれている場合があります。 ロゴをクリックすると、サイトのメインページ( index )が開きます。 画像の<img>タグとリンクの<a>タグを使用して、タグでマークアップできます。

 <header> <a href="/"> <img src="img.logo.png" alt="Logo"> </a> </header>

ロゴリンクとテキスト内の通常のリンクを区別するには、追加のスタイルが必要です。 次に、ロゴリンクから下線と青色を削除します。

 header a { ... }

ロゴリンクをメインページに表示する必要がないため、インデックスページのマークアップを変更します。

 <header> <!-- the <a> tag is replaced with <span> --> <span> <img src="img.logo.png" alt="Logo"> </span> </header>

<span>タグの下線と青色を削除する必要はありません。 それでは、さまざまなページからのロゴリンクの一般的なルールを作成しましょう。

 header a, header span { ... }

一見、このコードは問題ないように見えますが、デザイナーがレイアウトからロゴを削除した場合を想像してみてください。 セレクター名は、ロゴを使用してプロジェクトから削除する必要があるスタイルを理解するのに役立ちません。 「ヘッダーa」セレクターは、リンクとロゴの間の接続を表示しません。 このセレクターは、ヘッダーメニューのリンク、またはたとえば作成者のプロファイルへのリンクに属している可能性があります。 「ヘッダースパン」セレクターは、ヘッダーの任意の部分に属することができます。

混乱を避けるために、 logoクラスセレクターを使用してロゴスタイルを記述してください。

 .logo { ... }

CSSリセットは使用しません

CSSリセットは、ページ全体に対して作成された一連のグローバルCSSルールです。 これらのスタイルはすべてのレイアウトノードに影響を与え、コンポーネントの独立性に違反し、それらの再利用を困難にします。

BEMでは、「リセット」と「正規化」は1つのブロックにも使用されません。 リセットと正規化により、既存のスタイルがキャンセルされ、他のスタイルに置き換えられます。これらのスタイルは、いずれの場合も後で変更および更新する必要があります。 その結果、開発者は、リセットされたばかりのスタイルをオーバーライドするスタイルを作成する必要があります。

ユニバーサルセレクターは使用しません( *

ユニバーサルセレクターは、プロジェクトがレイアウト内のすべてのノードに影響を与えるスタイルを備えていることを示します。 これにより、他のプロジェクトでのレイアウトの再利用が制限されます。

  • さらに、アスタリスクの付いたスタイルをプロジェクトに転送する必要があります。 ただし、この場合、ユニバーサルセレクターが新しいプロジェクトのスタイルに影響を与える可能性があります。
  • アスタリスクの付いたスタイルは、転送するレイアウトに追加する必要があります。

さらに、ユニバーサルセレクターを使用すると、プロジェクトコードが予測不能になる可能性があります。 たとえば、ユニバーサルライブラリコンポーネントのスタイルに影響を与える可能性があります。

一般的なスタイルでは時間を節約できません。 多くの場合、開発者はコンポーネントのすべてのマージンをリセットすることから始めます( * { margin: 0; padding: 0; } )が、それでもレイアウトと同じように設定します(たとえば、 margin: 12px; padding: 30px; )。

ネストされたセレクターは使用しません

ネストされたセレクターはコードの結合を増やし、コードの再利用を困難にします。

BEM方法論では、ネストされたセレクターを禁止していませんが、あまり使用しないことをお勧めします。 たとえば、ブロックの状態または割り当てられたテーマに応じて要素のスタイルを変更する必要がある場合は、ネストが適切です。

 .button_hovered .button__text { text-decoration: underline; } .button_theme_islands .button__text { line-height: 1.5; }

複合セレクターは使用しません

結合されたセレクターは単一のセレクターよりも具体的であるため、ブロックの再定義がより困難になります。

次のコードを検討してください。

 <button class="button button_theme_islands">...</button>

.button.button_theme_islandsセレクターでCSSルールを設定して、書き込みを少なくするとします。 次に、「アクティブ」修飾子をブロックに追加します。

 <button class="button button_theme_islands button_active">...</button>

.button_activeセレクターは、 .button.button_theme_islands .button_active .button.button_theme_islandsされたブロックプロパティを再定義しません。 再定義するには、ブロック修飾子セレクターを.buttonセレクターと組み合わせて、 .button.button_theme_islandsの下で宣言します。これは、両方のセレクターが同じように固有であるためです。

 .button.button_theme_islands {} .button.button_active {}

単純なクラスセレクターを使用する場合、スタイルの再定義に問題はありません。

 .button_theme_islands {} .button_active {} .button {}

セレクターでタグとクラスを組み合わせることはありません

同じセレクター(たとえば、 button.button )でタグとクラスを組み合わせると、CSSルールがより具体的になるため、それらを再定義するのがより困難になります。

次のコードを検討してください。

 <button class="button">...</button>

button.buttonセレクターでCSSルールを設定するとします。 次に、 active修飾子をブロックに追加します。

 <button class="button button_active">...</button>

button.button.button_activeよりも具体的であるため、 .button_activeセレクターはbutton.buttonとして記述されたブロックプロパティを再定義しません。 より具体的にするには、ブロック修飾子セレクターをbutton.button_activeタグと組み合わせる必要があります。

プロジェクトが発展するにつれて、 input.buttonspan.button 、またはa.buttonセレクターを持つブロックになってしまう可能性があります。 この場合、 buttonブロックのすべての修飾子とそのネストされたすべての要素には、インスタンスごとに4つの異なる宣言が必要になります。

考えられる例外

まれに、この方法ではタグセレクターとクラスセレクターを組み合わせることができます。 たとえば、これは、正しいレイアウトを生成できないCMSシステムでコメントスタイルを設定するために使用できます。

コメントを使用して、テキストを書き込んだり、画像を挿入したり、マークアップを追加したりできます。 それらをサイトのデザインと一致させるために、開発者は、ユーザーが使用できるすべてのタグのスタイルを事前に定義し、ネストされたブロックにカスケードすることができます。

 <div class="content"> ... <!-- the user's text --> </div> CSS rules: .content a { ... } .content p { font-family: Arial, sans-serif; text-align: center; }

属性セレクターは使用しません

属性セレクターは、クラスセレクターよりも情報量が少なくなります。 証拠として、ヘッダーに検索フォームがある例を考えてみましょう。

 <header> <form action="/"> <input name="s"> <input type="submit"> </form> </header>

セレクター属性を使用してフォームスタイルを記述してみてください。

 header input[type=submit], header input[type=checkbox] { width: auto; margin-right: 20px; } header input[type=checkbox] { margin: 0; }

この例では、スタイルが検索フォームに属していることをセレクター名から確実に判断することはできません。 クラスを使用すると、より明確になります。 クラスには、明確に書くことを妨げる制限はありません。 たとえば、次のように書くことができます。

 .form .search { ... }

これで、コードのあいまいさが少なくなり、スタイルが検索フォームに属していることは明らかです。

ただし、ネストされたセレクターはCSSルールをより具体的にし、プロジェクト間でレイアウトを転送できないようにします。 ネストを取り除くには、BEMの原則を使用します。

概要classは、プロジェクト内の各コンポーネントのスタイルを分離できる唯一のセレクターです。 コードの可読性を高め、レイアウトの再利用を制限しないでください。

CSSスタイルの分離は、BEMジャーニーの最も頻繁な開始点です。 しかし、これはBEMが提供できる最小のものです。 分離された独立したコンポーネントがBEMでどのように配置されているかを理解するには、基本的な概念、つまり、ブロック、要素、修飾子、および混合を学習する必要があります。 次のセクションでこれを行いましょう。

BEMの基本

  • ブロックと要素
  • モディファイアとミックス
  • ファイル構造内のブロック

ブロックと要素

BEM方法論は、CSS、Sass、HTML、JavaScript、Reactなど、使用されているテクノロジーに関係なく適用できる一連のユニバーサルルールです。

BEMは、次のタスクの解決に役立ちます。

  • レイアウトを再利用します。
  • プロジェクト内でレイアウトフラグメントを安全に移動します。
  • 完成したレイアウトをプロジェクト間で移動します。
  • 安定した、予測可能で明確なコードを作成します。
  • プロジェクトのデバッグ時間を短縮します。

BEMプロジェクトでは、インターフェイスは要素を含めることができるブロックで構成されます。 ブロックは、ページの独立したコンポーネントです。 要素はブロックの外側に存在することはできないため、各要素は1つのブロックにのみ属することができることに注意してください。

BEMの最初の2文字は、 Bロックと要素を表します。 ブロック名は常に一意です。 要素の名前空間を設定し、ブロックパーツ間の目に見える接続を提供します。 ブロック名は長いですが、コンポーネント間の接続を示し、レイアウトを転送するときにこれらのコンポーネントの一部が失われないようにするために明確になっています。

BEMネーミングの全機能を確認するには、フォームを使用したこの例を検討してください。 BEM方法論によれば、フォームはformブロックを使用して実装されます。 HTMLでは、ブロック名はclass属性に含まれています。

 <form class="form" action="/">

それ自体では意味をなさないフォームのすべての部分( formブロック)は、その要素と見なされます。 したがって、検索ボックス( search )とボタン( submit )はformブロックの要素です。 クラスは、要素がブロックに属していることも示します。

 <form class="form" action="/"> <input class="form__search" name="s"> <input class="form__submit" type="submit"> </form>

ブロックの名前は、特別な区切り文字で要素の名前から区切られていることに注意してください。 BEMの従来の命名スキームでは、2つのアンダースコアが区切り文字として使用されます。 何でもセパレータとして機能できます。 別の命名規則があり、各開発者はそれらに適したものを選択します。 重要なことは、セパレーターを使用すると、プログラムでブロックを要素や修飾子から区別できることです。

セレクター名は、フォームを別のプロジェクトに移動するには、そのすべてのコンポーネントをコピーする必要があることを明確にしています。

 .form__search {} .form__submit {}

クラス名にブロックと要素を使用すると、重要な問題が解決されます。ネストされたセレクターを取り除くのに役立ちます。 BEMプロジェクトのすべてのセレクターの重みは同じです。 つまり、BEMに従って記述されたスタイルを再定義する方がはるかに簡単です。 これで、別のプロジェクトで同じフォームを使用するには、そのレイアウトとスタイルをコピーするだけです。

BEMコンポーネントの命名の考え方は、ブロックとその要素の間の接続を明示的に定義できるということです。

モディファイアとミックス

正式には、「M」はM odifierの略ですが、BEMのもう1つの重要な概念である「mix」も意味します。 モディファイアとミックスの両方が、ブロックとその要素に変更を加えます。 これを詳しく見てみましょう。

修飾子

モディファイアは、ブロックまたは要素の外観、状態、および動作を定義します。 修飾子の追加はオプションです。 モディファイアを使用すると、さまざまなブロック機能を組み合わせることができます。これは、モディファイアをいくつでも使用できるためです。 ただし、ブロックまたは要素に同じ修飾子の異なる値を割り当てることはできません。

モディファイアがどのように機能するかを調べてみましょう。

プロジェクトに上記の例と同じ検索フォームが必要だとします。 機能は同じですが、外観が異なる必要があります(たとえば、ページのヘッダーとフッターの検索フォームは異なる必要があります)。 フォームの外観を変更するために最初にできることは、追加のスタイルを作成することです。

 header .form {} footer .form {}

header .formセレクターは、 formセレクターよりも重要です。つまり、一方のルールがもう一方のルールをオーバーライドします。 しかし、すでに説明したように、ネストされたセレクターはコードの結合を増やし、再利用を困難にするため、このアプローチは機能しません。

BEMでは、修飾子を使用して、ブロックに新しいスタイルを追加できます。

 <!-- Added the form_type_original modifier--> <form class="form form_type_original" action="/"> <input class="form__search" name="s"> <input class="form__submit" type="submit"> </form>

<form class="form form_type_original"></form>の行は、ブロックにoriginal値のtype修飾子が割り当てられたことを示しています。 従来のスキームでは、修飾子名はブロック名または要素名からアンダースコアで区切られます。

フォームには、固有の色、サイズ、タイプ、またはデザインテーマを設定できます。 これらのパラメータはすべて、修飾子を使用して設定できます。

 <form class="form form_type_original form_size_m form_theme_forest"> <form class="form form_type_original form_size_m form_theme_forest">

同じフォームは異なって見える場合がありますが、同じサイズのままです。

 <form class="form form_type_original form_size_m form_theme_forest"></form> <form class="form form_type_original form_size_m form_theme_sun"></form>

ただし、各修飾子のセレクターの重みは同じです。

 .form_type_original {} .form_size_m {} .form_theme_forest {}

重要修飾子には、元のブロックの実装を何らかの方法で変更する追加のスタイルのみが含まれています。 これにより、ユニバーサルブロックの外観を一度だけ設定し、元のブロックコードとは異なる機能のみをモディファイヤスタイルに追加できます。

 .form { /* universal block styles */ } .form_type_original { /* added styles */ }

これが、モディファイヤが常にブロックおよびブロックが関連付けられている要素と同じDOMノード上にある必要がある理由です。

 <form class="form form_type_original"></form>

修飾子を使用して、非常に特殊な場合にユニバーサルコンポーネントを適用できます。 ブロックと要素のコードは変更されません。 必要な修飾子の組み合わせは、DOMノードで作成されます。

ミックス

ミックスを使用すると、同じフォーマットを異なるHTML要素に適用し、コードの重複を避けながら、複数のエンティティの動作とスタイルを組み合わせることができます。 それらは抽象ラッパーブロックを置き換えることができます。

混合とは、単一のDOMノードで複数のBEMエンティティ(ブロック、要素、修飾子)をホストすることを意味します。 モディファイアと同様に、ミックスはブロックの変更に使用されます。 ミックスを使用する必要がある場合の例をいくつか見てみましょう。

ブロックは、視覚的にだけでなく意味的にも異なる可能性があります。 たとえば、検索フォーム、登録フォーム、ケーキ注文フォームはすべてフォームです。 レイアウトでは、「フォーム」ブロックを使用して実装されていますが、共通のスタイルはありません。 このような違いを修飾子で処理することは不可能です。 このようなブロックに共通のスタイルを定義することはできますが、コードを再利用することはできません。

 .form, .search, .register { ... }

ミックスを使用して、同じフォームに対して意味的に異なるブロックを作成できます。

 <form class="form" action="/"> <input class="form__search" name="s"> <input class="form__submit" type="submit"> </form>

.formクラスセレクターは、任意のフォーム(注文、検索、または登録)に適用できるすべてのスタイルを記述します。

 .form {}

これで、ユニバーサルフォームから検索フォームを作成できます。 これを行うには、プロジェクトに追加のsearchクラスを作成します。 このクラスは検索のみを担当します。 .formクラスと.searchクラスのスタイルと動作を組み合わせるには、これらのクラスを単一のDOMノードに配置します。

 <form class="form search" action="/"> <input class="form__search" name="s"> <input class="form__submit" type="submit"> </form>

この場合、 .searchクラスは、動作を定義する別個のブロックです。 このブロックには、フォーム、テーマ、およびサイズを担当する修飾子を含めることはできません。 これらの修飾子はすでにユニバーサルフォームに属しています。 ミックスは、これらのブロックのスタイルと動作を組み合わせるのに役立ちます。

コンポーネントのセマンティクスが変更されたもう1つの例を見てみましょう。 これは、すべてのエントリがリンクであるページヘッダーのナビゲーションメニューです。

 <nav class="menu"> <a class="link" href=""></a> <a class="link" href=""></a> <a class="link" href=""></a> </nav>

リンク機能はすでにlinkブロックに実装されていますが、メニューリンクはテキスト内のリンクと視覚的に異なる必要があります。 メニューリンクを変更するには、いくつかの方法があります。

  1. エントリをリンクに変換するメニューエントリ修飾子を作成します。
     <nav class="menu"> <a class="menu__item menu__item_link" href=""></a> <a class="menu__item menu__item_link" href=""></a> <a class="menu__item menu__item_link" href=""></a> </nav>

    この場合、修飾子を実装するには、 `link`ブロックの動作とスタイルをコピーする必要があります。 これにより、コードが重複します。
  2. `link`ユニバーサルブロックと` menu`ブロックの `item`要素を組み合わせて使用​​します。
     <nav class="menu"> <a class="link menu__item" href=""></a> <a class="link menu__item" href=""></a> <a class="link menu__item" href=""></a> </nav>

    2つのBEMエンティティを組み合わせることで、 `link`ブロックからの基本的なリンク機能と` menu`ブロックからの追加のCSSルールを実装し、コードの重複を回避できるようになりました。

外部ジオメトリとポジショニング:抽象的なHTMLラッパーをあきらめる

ミックスは、他のブロックに対してブロックを配置したり、ブロック内の要素を配置したりするために使用されます。 BEMでは、ジオメトリと配置を担当するスタイルが親ブロックに設定されます。 ヘッダーに配置する必要のあるユニバーサルメニューブロックを見てみましょう。 レイアウトでは、ブロックは親ブロックから20pxのインデントを持っている必要があります。

このタスクにはいくつかの解決策があります。

  1. メニューブロックのインデントを使用してスタイルを記述します。
     .menu { margin-left: 20px; }

    この場合、「メニュー」ブロックはもはや普遍的ではありません。 メニューをページフッターに配置する必要がある場合は、インデントが異なる可能性があるため、スタイルを編集する必要があります。
  2. メニューブロック修飾子を作成します。
     <div> <ul class="menu menu_type_header"> <li class="menu__item"><a href=""></a></li> <li class="menu__item"><a href=""></a></li> <li class="menu__item"><a href=""></a></li> </ul> </div>
     .menu_type_header { margin-left: 20px; } .menu_type_footer { margin-left: 30px; }

    この場合、プロジェクトには2種類のメニューが含まれますが、そうではありません。 メニューは同じままです。
  3. ブロックの外部配置を定義します。すべてのインデントを設定する抽象ラッパー(たとえば、 `wrap`ブロック)に` menu`ブロックをネストします。
     <div class="wrap"> <ul class="menu"> <li class="menu__item"><a href=""></a></li> <li class="menu__item"><a href=""></a></li> <li class="menu__item"><a href=""></a></li> </ul> </div>

    修飾子を作成したり、ブロックスタイルを変更してブロックをページに配置したりする誘惑を回避するには、次の1つのことを理解する必要があります。

    親ブロックからのインデントは、ネストされたブロックの機能ではありません。 これは親ブロックの機能です。 ネストされたブロックは、特定のピクセル数だけ境界からインデントされる必要があることを知っている必要があります。
  4. ミックスを使用します。 ネストされたブロックの配置に関する情報は、親ブロック要素に含まれています。 次に、親ブロック要素がネストされたブロックに混合されます。 この場合、ネストされたブロックはインデントを指定せず、どこでも簡単に再利用できます。

例を続けましょう:

 <div> <ul class="menu header__menu"> <li class="menu__item"><a href=""></a></li> <li class="menu__item"><a href=""></a></li> <li class="menu__item"><a href=""></a></li> </ul> </div>

この場合、 menuブロックの外部ジオメトリと位置は、 header__menu要素を介して設定されます。 menuブロックはインデントを指定せず、簡単に再利用できます。

親ブロック要素(この場合はheader__menu )は、ブロックの外部配置を担当するラッパーブロックのタスクを実行します。

ファイル構造内のブロック

すべてのBEMプロジェクトは、同様のファイル構造を持っています。 使い慣れたファイル構造により、開発者はプロジェクトのナビゲート、プロジェクト間の切り替え、およびあるプロジェクトから別のプロジェクトへのブロックの移動が容易になります。

各ブロックの実装は、個別のプロジェクトフォルダーに保存されます。 各テクノロジー(CSS、JavaScript、テスト、テンプレート、ドキュメント、画像)は別々のファイルにあります。

たとえば、 inputブロックの外観がCSSで設定されている場合、コードはinput.cssファイルに保存されます。

 project common.blocks/ input/ input.css # The "input" block implementation with CSS input.js # The "input" block implementation with JavaScript

修飾子と要素のコードも、ブロックの個別のファイルに保存されます。 このアプローチでは、ブロックの実装に必要な修飾子と要素のみをビルドに含めることができます。

 project common.blocks/ input/ input.css # The "input" block implementation with CSS input.js # The "input" block implementation with JavaScript input_theme_sun.css # The "input_theme_sun" modifier implementation input__clear.css # The "input__clear" element implementation with CSS input__clear.js # The "input__clear" element implementation with JavaScript

プロジェクトのナビゲーションを改善するには、ブロック修飾子をディレクトリ内の複数の値と組み合わせます。

BEMプロジェクトのファイル構造は、再定義レベルで構成されています(詳細については、こちらをご覧ください)。 再定義レベルにより、次のことが可能になります。

  • プロジェクトをプラットフォームに分割します。
  • プロジェクトに含まれるブロックライブラリを簡単に更新します。
  • 共通のブロックを使用して複数のプロジェクトを開発します。
  • プロジェクトロジックに影響を与えずにデザインテーマを変更します。
  • ライブプロジェクトで実験を行います。

ブロックを使用し、すべてのブロックテクノロジーを同じフォルダーに保存すると、プロジェクト間でブロックを簡単に移動できます。 ブロックのすべてのスタイルと動作をレイアウトと一緒に移動するには、ブロックフォルダーを新しいプロジェクトにコピーするだけです。

方法論の明白でない利点

並行開発の利便性

BEMでは、レイアウトはすべてブロックに分割されます。 ブロックは独立しているため、複数の開発者が並行して開発できます。

開発者は、他のプロジェクトで再利用できるユニバーサルコンポーネントとしてブロックを作成します。

例として、リンク、ボタン、入力フィールドなどのユニバーサルブロックを含むbem-componentsブロックライブラリがあります。 ユニバーサルコンポーネントからより複雑なブロックを作成する方が簡単です。 たとえば、セレクターまたはチェックボックス。

プロジェクトレイアウトでブロックを使用すると、複数の開発者が作成したコードの統合にかかる時間を節約し、コンポーネント名の一意性を保証し、開発段階でブロックをテストできます。

レイアウトのテスト

特にデータベースに接続された動的プロジェクトでは、ページ全体の機能をテストするのは問題があります。

BEMでは、各ブロックがテストの対象になります。 テストは、JavascriptやCSSなどのブロック実装テクノロジーです。 ブロックは開発段階でテストされます。 1つのブロックの正しさを確認してから、テストしたブロックからプロジェクトを組み立てる方が簡単です。 その後、あなたがしなければならないのは、ブロックラッパーが正しく機能していることを確認することです。

プロジェクトのカスタマイズ可能なビルド

便利な開発のために、BEMプロジェクトのすべてのブロックとテクノロジーは別々のフォルダーとファイルに配置されます。 ソースファイルを1つのファイルに結合するには(たとえば、すべてのCSSファイルをproject.cssに、すべてのJSファイルをproject.jsに配置するなど)、ビルドプロセスを使用します。

ビルドは次のタスクを実行します。

  • プロジェクトのファイルシステム全体に分散しているソースファイルを結合します。
  • プロジェクトに必要なブロック、要素、および修飾子(BEMエンティティ)のみが含まれます。
  • エンティティを含めるための順序に従います。
  • ビルド中にソースファイルコードを処理します(たとえば、LESSコードをCSSコードにコンパイルします)。

ビルドに必要なBEMエンティティのみを含めるには、ページで使用されるブロック、要素、および修飾子のリストを作成する必要があります。 このリストは宣言と呼ばれます。

BEMブロックは独立して開発され、ファイルシステム内の別々のファイルに配置されるため、お互いについて何も「知りません」。 他のブロックに基づいてブロックを構築するには、依存関係を指定します。 これを担当するBEMテクノロジーがあります: deps.jsファイル。 依存関係ファイルは、プロジェクトに含める必要のある追加のブロックをビルドエンジンに通知します。

実用的なケース:BEMはCSSだけのものではありません

前のセクションでは、すべてのコード例はCSS用です。 ただし、BEMを使用すると、CSSの場合と同じ宣言的な方法で、ブロックの動作とHTMLでのその表現を変更できます。

BEMでテンプレートを使用する方法

HTMLでは、ブロックがページに表示されるたびにブロックマークアップが繰り返されます。 HTMLマークアップを手動で作成してから、エラーを修正したり変更したりする必要がある場合は、ブロックのすべてのインスタンスのマークアップを変更する必要があります。 HTMLコードを生成し、修正を自動的に適用するために、BEMはテンプレートを使用します。 ブロックは、HTMLでの表示方法に責任があります。

テンプレートを使用すると、次のことができます。

  • テンプレートの変更はすべてのプロジェクトブロックに自動的に適用されるため、プロジェクトのデバッグに使用する時間を短縮できます。
  • ブロックレイアウトを変更します。
  • 現在のレイアウトのブロックを別のプロジェクトに移動します。

BEMは、次の2つのエンジンを備えたbem-xjstテンプレートエンジンを使用します。

  • BEMHTML
    ページのBEMJSON記述をHTMLに変換します。 テンプレートは.bemhtml.jsファイルで説明されています。
  • ベントリー
    データをBEMJSONに変換します。 テンプレートは、 .bemtree.jsファイルにBEMJSON形式で記述されています。

テンプレートがブロック用に作成されていない場合、テンプレートエンジンはデフォルトでブロック用の<div>タグを設定します。

ブロックの宣言とHTML出力を比較します。

宣言:

 { block: 'menu', content: [ { elem: 'item', content: { block: 'link'} }, { elem: 'item', elemMods: { current: true }, // Set the modifier for the menu item content: { block: 'link' } } ] }

HTML:

 <div class="menu"> <div class="menu__item"> <div class="link"></div> </div> <div class="menu__item menu__item_current"> <div class="link"></div> </div> </div>

menuブロックのレイアウトを変更するには、ブロックのテンプレートを作成する必要があります。

  1. menuブロックタグを変更してみましょう。
     block('menu')( tag()('menu') // Set the "menu" tag for the menu block )

    変更されたHTML:
     <menu class="menu"> <!-- Replace the "div" tag with the "menu" tag for the "menu" block --> <div class="menu__item"> <div class="link"></div> </div> <div class="menu__item menu__item_current"> <div class="link"></div> </div> </menu>

    CSSと同様に、テンプレートはページ上のすべての「メニュー」ブロックに適用されます。
  2. 内部ラッパーとして機能し、 menuブロック内の要素のレイアウトを担当する要素( menu__inner )を追加します。 元々、 menu__inner要素は宣言に含まれていなかったため、テンプレートの作成時に追加する必要があります。

    BEMテンプレートはJavaScriptで記述されているため、JavaScriptを使用してテンプレートに新しい要素を追加することもできます。
     block('menu')( tag()('menu'), content()(function() { return { elem: 'inner', content: this.ctx.content }; }) )
     <menu class="menu"> <!-- Replace the "div" tag with the "menu" tag for the "menu" block --> <div class="menu__inner"> <div class="menu__item"> <div class="link"></div> </div> <div class="menu__item menu__item_current"> <div class="link"></div> </div> </div> </menu>
  3. すべてのinner要素とitem要素のタグを置き換えます。
     block('menu')( tag()('menu'), content()(function() { return { elem: 'inner', content: this.ctx.content } }), elem('inner')( tag()('ul') ), elem('item')( tag()('li') ) )
     <menu class="menu"> <ul class="menu__inner"> <li class="menu__item"> <div class="link"></div> </li> <li class="menu__item menu__item_current"> <div class="link"></div> </li> </ul> </menu>
  4. ページ上のすべてのリンクに<a>タグを設定します。
     block('menu')( tag()('menu'), content()(function() { return { elem: 'inner', content: this.ctx.content } }), elem('inner')( tag()('ul') ), elem('item')( tag()('li') ) ); block('link')( tag()('a') );
     <menu class="menu"> <ul class="menu__inner"> <li class="menu__item"> <a class="link"></a> </li> <li class="menu__item menu__item_current"> <a class="link"></a> </li> </ul> </menu>
  5. 既存のテンプレートを変更します。 テンプレートのルールは、CSSの場合と同じ方法で適用されます。つまり、低いルールが高いルールをオーバーライドします。 テンプレートに新しいルールを追加し、リンクタグを<a>から<span>に変更します。
     block('link')( tag()('a') ); block('link')( tag()('span') );
     <menu class="menu"> <ul class="menu__inner"> <li class="menu__item"> <span class="link"></span> </li> <li class="menu__item menu__item_current"> <span class="link"></span> </li> </ul> </menu>

BEMはカスタマイズ可能なシステムです

BEM方法論は、プロジェクトでシステムを作成するための厳密なルールを提供します。 しかし同時に、多くのBEMルールをカスタマイズできます。 BEM手法では、命名規則を変更したり、最も便利なファイル構造を選択したり、ブロックに必要なテクノロジーを追加したりできます。

これで、システムを調整して、BEMの独自のスーパーヒーローを作成できます。

キャプテンアメリカのロゴとしてのBEM
BEMキャプテンアメリカ

BEMを最大限に活用する方法

BEMの原則の学習を開始するには、当社のWebサイトにアクセスしてください。 チームに質問したい質問がある場合は、Telegramチャネルに参加するか、BEMフォーラムでディスカッションを開いてください。