テンプレートエンジン{Mustache}を使ってみた

スタッフブログ

こんにちは。最近時間の流れが速く感じる、森山です。今回はロジックレステンプレートエンジンの1つである「Mustache」を使う機会があったため紹介していきたいと思います。

紹介

このMustacheというテンプレートエンジンはさまざまな環境で使えるのですが、今回は使い慣れているJavaScriptのnpmパッケージ版を使って説明します。

npmパッケージについて

導入方法

npmインストールするということで、node.js等のnpmコマンドが実行できる環境が必要です。npmにて公開されているMustacheは「mustache」で公開されているので、npm i -s mustacheを実行することでインストールすることができます。
後はMustacheを使用したいjsファイルでimportrequire等を使用してMustacheを読み込めば使用できます。

関数説明

Mustacheにはrenderメソッドが実装されていて、引数には2つの値を受け取ることができます。第一引数にはテンプレート文字列(変換したい文字列)を渡して、第二引数には変換の情報が入ったオブジェクトを渡します。

const data = {
    food: "バナナ",
};

const out = Mustache.render("私の好きな食べ物は{{food}}です。", data);
// 私の好きな食べ物はバナナです。

機能紹介

Mustacheでは主に、

  • Variables
  • Sections
  • Inverted Sections
  • Comments
  • Set Delimiter

という機能があります。

Variables(変数)

まずは基本となるVariablesですが、これはその名の通り値を代入してくれる機能です。書きかたとしては、私の好きな食べ物は{{food}}ですといったように {{ }} で囲った中の文字を変数名として代入してくれます。これを今回の環境で書くとこのようになります。

const data = {
    food: "バナナ",
};

const out = Mustache.render("私の好きな食べ物は{{food}}です。", data);
// 私の好きな食べ物はバナナです。

今回の例で言うと、"私の好きな食べ物は{{food}}です。"dataのプロパティの"バナナ"が渡されて私の好きな食べ物はバナナです。という1つの文字列になりました。

ここで気を付ける点として、変数として渡す文字列はHTMLエスケープされて出力されるということです。たとえばfoodバナナ&リンゴを入れて実行してみると、私の好きな食べ物はバナナ&リンゴです。という風にエスケープされてしまいます。これはHTML上で予期しないコードが実行されるのを防ぐためにも大切な機能なのですが、通常通り出力したい場合に困ります。そんな時は波括弧を2つではなく3つで囲う、もしくは波括弧の先頭に&を書くとエスケープされずに出力できます。

const data = {
    food: "バナナ&リンゴ",
};

const out1 = Mustache.render("私の好きな食べ物は{{{food}}}です。", data);
// 私の好きな食べ物はバナナ&リンゴです。
const out2 = Mustache.render("私の好きな食べ物は{{&food}}です。", data);
// 私の好きな食べ物はバナナ&リンゴです。

Sections(節、セクション)

Sectionsは波括弧の先頭に#を付けた場所から始まり、先頭に/が付いている同じ変数の間までのことです。このSectionsは変数に配列、関数、それ以外のfalsyな値、truthyのどれを渡すかで動作が大きく変わります。

False Values or Empty Lists(falseもしくは空の配列)

Sectionsの変数へfalseもしくは空の配列が入っていたときに、Sections内にある文字列は出力されません。

const text = "私は{{#modifier}}普段あまり{{/modifier}}テレビを見ません。";
const out1 = Mustache.render(text, { modifier: false });
// 私はテレビを見ません。
const out2 = Mustache.render(text, { modifier: [] });
// 私はテレビを見ません。

Non-False Values(false以外の値)

変数がfalse以外かつ配列でない場合は中身を通常通り表示します。true(false以外の値)とfalseを使えば、セクション内の文字列をif文を使ったように表示・非表示を切り替えることができます。

const data = {
    show: true,
    hide: false,
};

const out = Mustache.render("{{#show}}表示されます{{/show}}{{#hide}}表示されません{{/hide}}", data);
// 表示されます

Non-Empty Lists(空ではない配列)

変数が空でない配列だった場合配列の要素を使ってループします。この時変数以外に文字があった場合はそれも繰り返されます。

const data = {
    drink: [
        {tea: "麦茶"},
        {tea: "ほうじ茶"},
        {tea: "緑茶"},
    ],
};

const out = Mustache.render("私の好きなお茶は{{#drink}}{{tea}}と{{/drink}}です。", data);
// 私の好きなお茶は麦茶とほうじ茶と緑茶とです。

Lambdas(ラムダ)

変数に関数を渡すと、レンダリングされていない文字列リテラルとレンダリングする関数を引数に渡されます。これを用意した関数内で使用して組み立て、返り値で出力したい値を渡せばその文字列が出力されます。

const data = {
    genre: "お茶",
    func: () => (text, render) => {
        const teas = ["麦茶", "ほうじ茶", "緑茶"];
        let str = "";
        for (const tea of teas) {
            if (str) {
                str += "と" + tea;
            } else {
                str += tea;
            }
        }
        return render(text) + str;
    },
};

const out = Mustache.render("私の好きな{{genre}}は{{#func}}{{tea}}{{/func}}です。", data);
// 私の好きなお茶は麦茶とほうじ茶と緑茶です。

Inverted Sections(反転したセクション)

Sectionsの宣言に#を使っている部分を^へ変更する事でSectionsの判定を反転できます。

const data = {
    show: false,
};

const out = Mustache.render("{{^show}}表示されます{{/show}}{{#show}}表示されません{{/show}}", data);
// 表示されます

Comments(コメント)

波括弧の内側の先頭に!を入力すると必ず消去される、コメントの機能があります。

const out = Mustache.render("マウスとキーボード{{!無いと困る}}は重要", data);
// マウスとキーボードは重要

Set Delimiter(デリミターの設定)

波括弧の内側に=で囲って新しいタグを指定する事でそこから違うタグでMustacheを実行できます。変更した所から初期値で設定されていた {{ }} は認識しなくなります。

const data = {
    text1: "「文章1」",
    text2: "「文章2」",
};

const out = Mustache.render("1.{{text1}} 2.{%text2%} 記号変換{{={% %}=}} 3.{{text1}} 4.{%text2%}", data);
// 1.「文章1」 2.{%text2%} 記号変換 3.{{text1}} 4.「文章2」

Mustacheの実際の使い道

Mustacheにはこのように色々な機能がありますが、今回私が実際に使って便利だと思ったのはSections(節、セクション)のtrue,false機能です。今回は配置先ごとに応じて処理を分ける方法を実装したかったのですが、始めはJavaScript等を使って実行しないようにすることも考えました。しかし、Mustacheを使えばビルド時の処理時間が増えるだけで使用者側には無駄な処理が増えないので良いなと思ったので採用しました。

    <!-- GOOGLE_ANALYTICSにfalseが入っていれば完全に消すことができる -->

    <!-- {{#GOOGLE_ANALYTICS}} -->
    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id="></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag() { dataLayer.push(arguments); }
        gtag('js', new Date());
        gtag('config', '');
    </script>
    <!-- {{/GOOGLE_ANALYTICS}} -->

まとめ

Mustacheは他の環境でも同じ記法が使えるので別環境で同じ処理をするときに覚え直す内容が少なくなるというのは良いと思います。