k4200’s notes and thoughts

Programmer side of k4200

Mustach Template Engine

はじめに

JavaScriptPHPでテンプレートを共用したい

今仕事で作ってるサイトは、フレームワークとかも使っていないプレーンなPHPで出来ている。(自分は開発に途中から参加したので、その辺りの決定には関与していない)。

あと、特徴として、(最近のwebサイトにありがちな)サーバーからJSON形式のデータをAJAXで取得してJavaScriptで描画する処理が結構多い。その部分の処理にテンプレートを使って楽をしたいと思って、最初はJavaScriptで使えるテンプレートをいくつか試した。

そのうちSEOとかも絡んでくると、クライアント側のAJAXで処理するだけではなく、時にはサーバー側(PHP)で全てHTMLを組み立ててブラウザに返す場合も出てきた。となると、PHPJavaScriptで同じテンプレートが使えたほうが楽じゃんって事でMustache Template Engine (System?)を使うことにした。

Mustache?

詳しくはググるなりbingるなりして下さい。

  • 各言語向けの実装があること
  • Logic-lessという、ある意味大胆なシンプルさ

の2点が特徴かと個人的には思ってる。Logic-lessっていうのは、他のテンプレートエンジンにあるようなif, else, whileのような制御構造がないって意味なんだけど、これには抜け道というかそういうのがあるので、別にそんなに使い勝手が悪いわけでもない。詳しくは(?)後述。

基本的な使い方

準備

ここから各言語向けの実装が落とせるので、PHP用のMustache.phpJavaScript用の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は手軽でそこそこ便利だよ。性能もそこそこだったはずなので、大規模サイトとかの場合には違った選択肢もあるとは思うけど。詳しくは「テンプレートエンジン パフォーマンス」などで検索するといいかも。