Mustach Template Engine
はじめに
JavaScriptとPHPでテンプレートを共用したい
今仕事で作ってるサイトは、フレームワークとかも使っていないプレーンなPHPで出来ている。(自分は開発に途中から参加したので、その辺りの決定には関与していない)。
あと、特徴として、(最近のwebサイトにありがちな)サーバーからJSON形式のデータをAJAXで取得してJavaScriptで描画する処理が結構多い。その部分の処理にテンプレートを使って楽をしたいと思って、最初はJavaScriptで使えるテンプレートをいくつか試した。
そのうちSEOとかも絡んでくると、クライアント側のAJAXで処理するだけではなく、時にはサーバー側(PHP)で全てHTMLを組み立ててブラウザに返す場合も出てきた。となると、PHPとJavaScriptで同じテンプレートが使えたほうが楽じゃんって事でMustache Template Engine (System?)を使うことにした。
Mustache?
詳しくはググるなりbingるなりして下さい。
- 各言語向けの実装があること
- Logic-lessという、ある意味大胆なシンプルさ
の2点が特徴かと個人的には思ってる。Logic-lessっていうのは、他のテンプレートエンジンにあるようなif, else, whileのような制御構造がないって意味なんだけど、これには抜け道というかそういうのがあるので、別にそんなに使い勝手が悪いわけでもない。詳しくは(?)後述。
基本的な使い方
準備
ここから各言語向けの実装が落とせるので、PHP用のMustache.phpとJavaScript用のmustache.jsをダウンロードしておく。
必須じゃないけど便利なんでjQueryもダウンロードしておく。
テンプレートの定義
PHPで以下のように定義する。これを例えばtmpl.inc.phpとして保存する。
$tmpl = <<EOT <div class="image_area"> <img id="image_{{image_id}}" src="{{url}}" /> </div> EOT;
同じテンプレートをJavaScript側でも使えるように、以下のようにする。
<?php require_once('tmpl.inc.php'); ?> var tmpl = <?php print json_encode($tmpl); ?>;
PHPで使用する
<?php require_once('Mustache.php'); //ライブラリの読み込み $m = new Mustache(); // インスタンスの生成 // テンプレートに流しこむデータの作成 $data = array("image_id" = 100, "url" => "http://image.example.com/100.jpg"); ?> <html> <head><title>PHPのサンプル</title></head> <body> <div id="canvas"> <?php $m->render($tmpl, $data); // 描画 ?> </div> </body> </html>
JavaScriptで使用する
<?php require_once('tmpl.inc.php'); ?> <html> <head> <title>JavaScriptのサンプル</title> <script type="text/javascript" src="jquery.min.js"></script> <script type="text/javascript" src="mustache.js"></script> </head> <script> $(document).ready(function() { var tmpl = <?php print json_encode($tmpl); ?>; var data = {image_id: 100, url: "http://image.example.com/100.jpg"}; var htmlStr = Mustache.to_html(tmpl, data); // HTMLの生成 $('#canvas').html(htmlStr); // 生成されたHTMLをdiv要素に挿入 }); </script> <body> <div id="canvas"></div> </body> </html>
ロジック風のもの、その1:ループ
こっからはサクサク説明。まずは複数の画像を表示させてみる。
テンプレート
{{#foo}} というセクションは、渡されたデータにfooという名前の配列があれば、その各要素に関してテンプレートを展開する。具体例を。
$tmpl = <<EOT <div class="image_area"> {{#images}} <img id="image_{{image_id}}" src="{{url}}" /> {{/images}} </div> EOT;
PHP
プログラムはデータの部分のみ変更で、その他は一緒。
$data = array("images" => array( array("image_id" = 100, "url" => "http://image.example.com/100.jpg"), array("image_id" = 103, "url" => "http://image.example.com/103.gif")));
JavaScript
書くまでもないけど・・・
var data = {images: [{image_id: 100, url: "http://image.example.com/100.jpg"}, {image_id: 103, url: "http://image.example.com/103.jpg"}]};
ロジック風のもの、その2:条件分岐
英語のWikipediaのMustacheのページを見ると、「ラムダを使えば出来るよ」って書いてあるし、確かにその通りなんだけど、よくあるような「偶数行と奇数行で色分けしたい」とかなら、以下のようなもうちょい簡単な方法で良さそう。
テンプレート
{{#even}}~{{/even}}で囲まれたセクションは、evenという変数が存在してtrueであれば描画される。{{^even}}~{{/even}}はその逆。
$tmpl = <<EOT <table> <tr> <th>製品コード</th><th>製品名</th> </tr> {{#products}} {{#even}} <tr class="gray"> {{/even}} {{^even}} <tr class="white"> {{/even}} <td>{{product_id}}</td> <td>{{product_name}}</td> </tr> {{/products}} </table> EOT;
PHP
先程と同様、プログラムはデータの部分のみ変更で、その他は一緒。
$data = array("products" => array( array("product_id" = 33, "product_name" => "ああ", "even" => true), array("product_id" = 35, "product_name" => "いい"), array("product_id" = 12, "product_name" => "うう", "even" => true)));
JavaScript
var data = {products: [{product_id: 33, product_name: "ああ", even: true}, {product_id: 35, product_name: "いい"}, {product_id: 12, product_name: "うう", even: true}]};
その他紹介していない事柄を簡単に説明
エスケープ
{{foo}} は全てエスケープされるので、XSS対策によい。というか、それがそもそもテンプレートエンジンを使う意義の1つだし。ちなみに{{{foo}}} (カッコが1つ多い)とやるとエスケープされなかったはず。うろ覚え。
パーシャル
これは便利。テンプレートAの中でサブテンプレートBを呼び出す、みたいな感じ。このエントリーをここまで読んでいれば、後はマニュアル見ればすぐ使い方はわかるはず。
まとめ
Mustacheは手軽でそこそこ便利だよ。性能もそこそこだったはずなので、大規模サイトとかの場合には違った選択肢もあるとは思うけど。詳しくは「テンプレートエンジン パフォーマンス」などで検索するといいかも。