Stimulus中のHTML生成を避ける理由

HTML生成を避けるのは自主ルール

Stimulus Controllerの中でHTMLを生成することは可能です。JavaScriptにはそのためのメソッドが多数用意されています。しかしJavaScriptによるHTML生成を自主的に避けるのがStimulusの流儀です。

その理由を私なりに紹介し、いつならば許容できるかを考えます。

ERBとのコードの重複を避ける

Rail/ERBではサーバサイドでHTMLをレンダリングするのが基本となります。同じHTMLをStimulusでも生成してしまうと、2箇所で同じを記述することになってしまいます。これはコードの重複になり、メンテナンス上の問題になります。 Stimulus Controllerの中でHTMLレンダリングを避けるのは、このコード重複を避けるためです。

ブラウザでのHTML生成の主な負担はJSONの準備にある

すでにJSONが用意されている場合ならHTMLの生成は簡単です。JSXにJSONを流し込むだけで十分です。しかしブラウザでHTMLをレンダリングする際にしばしば問題になるのはJSONの準備です。簡単なものであれば問題ありませんが、大きなHTMLをブラウザで生成する場合、特にSPAの場合はJSONの準備が大きな負担になります。

HTML生成が許容される場合

コード重複の影響が少ない場合や、コード重複が避けられる場合はStimulus controllerでHTMLを生成しても問題ありません。

  • 生成するHTMLが少ない場合: 例えば画面上の文言を修正したりする程度であればコード重複の影響は少ないので、Stimulus controllerでHTMLを生成しても問題ありません。
  • <template>タグ <template>タグを利用すると、サーバ側でHTMLのテンプレートを事前にレンダリングすることが可能です。コードの重複は発生しませんので、問題ありません。
  • もちろん上記は絶対的な基準ではなく、実際に用途に合わせて柔軟に考えたら良いと思います。

CSSでHTML生成を避ける方法

Stimulusでは主にCSSでHTML要素の表示状態を制御し、HTML生成を不要にします。実際のコードを見ながら解説します。

Reactの場合

jsx
function Item({ name, isPacked }) {
  if (isPacked) {
    return <li className="item">{name}</li>;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}
  • Reactでは条件付きレンダーが推奨されていますので、Itemコンポーネントに渡すisPacked propsで表示の有無を制御します。
  • Itemコンポーネントの中ではisPackedのtrue/falseを見て、異なるDOMをレンダーします。

CSSの場合

html
<style>
  .item__check {
    display: none;
  }
  [data-checked="true"] .item__check {
    display: inline-block;
  }
</style>

<section>
  <h1>Sally Ride's Packing List</h1>
  <ul>
    <li class="item" data-checked="true">
      Space suit<span class="item__check"></span>
    </li>
    <li class="item" data-checked="true">
      Helmet with a golden leaf<span class="item__check"></span>
    </li>
    <li class="item">
      Photo of Tam<span class="item__check"></span>
    </li>
  </ul>
</section>
  • Stimulusの例ではCSSを使います。
  • <li>data-checked属性を変更することでitem__checkの表示のオン・オフを切り替えています。
  • Stimulus Controllerは各<li>を描画するかしないかを制御するのではなく、data-checked属性を切り替えて、表示・非表示を切り替えています。

なおdata-*属性を変えるのではなく、Stimulusから直接CSSクラスを書き換える方法もあります。しかし表示に関するCSSクラスと状態に関するCSSクラスの見分けがつかなくなる問題点があります。data-*属性を使って、表示とステートを分けることをお勧めします。