HTMLのコンポーネント化は、HTMLとCSSに「関数」概念を持ち込むことに他なりません。Bootstrapを使ったことがあれば、<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenuDivider">
とか毎回手書きする苦行を知っていると思います。「こんなんやってられっか」って話ですよ。
この記事では、Bootstrapをコンポーネント化してみて、HTMLの書き方がどう変わるかその実際を書き留めておきたいと思います。まだ、HTML/CSS陣営からはあまりコンポーネントの話が出てこないですが、数ヶ月先(?)に迫った未来の話として、HTML/CSSが主戦場の人に眺めてもらえたら本望です。
HTMLなのにリーダブル
残念ながら名著「リーダブルコード」にはHTMLに関する章がありません。なぜなら、HTMLは美しく書きようがないからです(爆)。どうしても冗長な表現を常に書き続ける必要がありました。少なくとも「コンポーネント」以前は。
でも、実際に実装してみてわかったことは、HTMLはこんなにもわかりやすく書けたのか、ということ。「HTMLなのにリーダブル」これに尽きます。次にいくつか例を挙げてみます。
以下、Bootstrap本家と、今回のRiot.js実装(後述)の比較を並べて行きます。なお、ここに挙げているRiot.jsの例は、まだ技術プレビューの段階です。(実戦投入はできないので悪しからず)
ボタン
<button class="btn btn-default" type="button">Button</button>
↑が、↓に。※上: Bootstrap、下: Riot.jsによる試験実装 (以下同じ)
<btn>Button</btn>
ボタングループ
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-default">Left</button>
<button type="button" class="btn btn-default">Middle</button>
<button type="button" class="btn btn-default">Right</button>
</div>
↑が、↓に。
<btn-group>
<btn>Left</btn>
<btn>Middle</btn>
<btn>Right</btn>
</btn-group>
ドロップダウン
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
Dropdown
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenuDivider">
<li role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
<li role="presentation" class="divider"></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
</ul>
</div>
↑が、↓に。これなら、コピー&ペーストしなくても書けそうです。
<btn-group>
<btn toggle="menu">Default <caret /></btn>
<menu>
<menu-item value="action">Action</menu-item>
<menu-item value="another">Another action</menu-item>
<menu-divider></menu-divider>
<menu-item value="separated">Separated link</menu-item>
</menu>
</btn-group>
Bootstrapをコンポーネント化してみた
上述のように、BootstrapコンポーネントをRiot.js上に実装し直しています(途中)。ボタン、ボタングループ、ドロップダウンあたりはサクッとできました。主目的は、Riot.jsのユースケースと未実装機能の洗い出しなので、すべてを移植するかは不透明ですが、ちょこちょこコンポーネントを追加していこうと思っています。(追加PRも歓迎です)
(元ネタはReact Bootstrapですが、Riot.js版はもう少し素直な構成にしています。元のBootstrapのHTMLに近い方向性で)
実装の戦略は90度違う
単純化のために、次の3つの機能があったとして、従来Bootstrapで取っている戦略は「行ごと」にまとめることでした(水平分業)。コンポーネントの場合、これが「列ごと」にまとめて扱うことになります(垂直統合)。要は、今まで水平分業していたのが垂直統合に変わるイメージですね。
機能単位 | ボタン | ボタングループ | メニュー |
---|---|---|---|
HTML | ボタンのHTML | グループのHTML | メニューのHTML |
CSS | ボタンのCSS | グループのCSS | メニューのCSS |
JavaScript | ボタンのJavaScript | グループのJavaScript | メニューのJavaScript |
このことでモジュール性が高まり、実装も圧倒的にシンプルになります。コンポーネントという手頃な粒度になること、セレクタによるノード指定がほぼ不要になること(jQueryも不要)が大きく寄与しています。(体感としては、コード量が4割減。例えばキャレット(三角マーク)の実装はこれだけ)
つまり、「コンポーネント実装もリーダブル」になります。
まとめ
さようなら、クラス。こんにちはリーダブル・コード。...になってほしい。