読者です 読者をやめる 読者になる 読者になる

望月いちろうのREADME.md

書き溜めておいた技術記事や旅行記のバックアップです。

ejs | PHPライクなNode.jsのテンプレートエンジン

f:id:mochizuki_p:20170111165510p:plain

PHPライクなNode.js用テンプレートエンジン

Webアプリケーション開発の生産性を飛躍的に上昇させ、もはや必要不可欠とも言えるのが

テンプレートエンジンです

その中でも、今回は定評のあるejsを紹介しようと思います。

Expressでの利用を前提に説明します。

公式

github.com

EJS -- Embedded JavaScript templates

特徴

まずはその特徴について

  • テンプレートのキャッシュに対応
  • テンプレートの変更に応じて差分を取る
  • JavaScriptの文法
  • 複数行にも対応
  • Atomでのハイライトにも対応

キャッシュに対応

これはテンプレートを利用するたびに毎回ROMから読み込むことなく、メモリに載せておきます。

キャッシュを有効化するには明示的に設定する必要があります。

こうすることで非常に高速に動作するのですね

JavaScriptの文法

ejsはHTMLの中にJavaScriptの文法で条件やループを記述できます。 PHPのような感覚でしょうか?

PHPの場合

$content = "Hello World";
<p>
<?php echo $content; ?>
</p>

結果

<p>
Hello World
</p>

ejsの場合

var content = "Hello World";
<p>
<%= content %>
</p>

結果

<p>
Hello World
</p>

少しアドホックな感じもしますが、テンプレートエンジンごときにあまりこだわる必要はないということでしょうか?

Atomでのハイライトにも対応

GitHub特製のテキストエディタであるAtomでは

ejsのハイライト用プラグインが利用可能です。

基本的な使い方

導入

Expressアプリケーションのホームディレクトリで

sudo npm install ejs

とすればOKです。

Expressで利用する

Expressでのデフォルトのテンプレートエンジンはjadeになっています。

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

これをejsに変更してみましょう.

express-generatorでプロジェクトを初期化する際にejsに変更します。

express --ejs --view=ejs test

// view engine setup

app.set('views', path.join(__dirname, 'views'));

app.set('view engine', 'ejs');

これでテンプレートエンジンがjadeからejsに切り替わりました。

テンプレートの利用

特定のパスに対して、テンプレートhogeを利用するには以下の形式で指定します。

router.get("/hoge",(req,res,next)=>{

   return res.render("hoge",{title:"Hello ejs"});
});

大事なのがrender()メソッドです。ここでは、第一引数でテンプレートを指定して、第二引数(オブジェクト)で値をテンプレートに渡します。

これを以下のように編集してみましょう。

<!DOCTYPE html>
<html>

    <head>
        <title> <%- title %> </title>
    </head>
    
    <body>
             <h1> <%- title %> </h1>
    </body>

</html>

そして保存して、ホームディレクトリに移動、そしてExpressを起動してください。

そしてGET /hogeにアクセス

http://localhost:3000/hoge

すると

<!DOCTYPE html>
<html>

    <head>
        <title> Hello ejs </title>
    </head>
    
    <body>
             <h1> Hello ejs </h1>
    </body>

</html>

となりました。

つまり、hoge.ectで<%- title %>となっていた部分が

render()で渡した値に置き換わっているわけですね。

出力

ejsは様々なタイプの出力に対応しています。

一覧

  • <%
  • <%=
  • <%-
  • <%#
  • <%%
  • %>
  • -%>
<% JavaScriptを利用する

PHPにおける

<?php ~ ?>

と似た意味をもった構文です。

特定の値を出力することはありません

通常はifやforなどの構文を記述するために使われます。

<% if(flag) { %>
<%- エスケープなし

エスケープをせずに出力を行います。

 <%- @title %>
<%= エスケープあり

なしの場合に比べて余分に時間がかかります。

 <%= @title %> 
<%# コメントを記述

コメントを記述するためのタグです。

出力後のhtmlには反映されません

<%# コメント %>

出力結果




<%% <%を出力結果に出したい時に使用する

通常のejsファイル中に<%を記述すると、出力結果には反映されません。

もし、出力ファイル(html)中に<%のリテラルを含めたい時にはこの構文を利用します

<%% Hello World %>

出力結果

<% Hello World %>
%> 改行をして出力
<%= "Hello World" %>
<%= "Hello World" %>
Hello World
Hello World 
-%> 改行をせずに出力
<%= "Hello World" -%>
<%= "Hello World" -%>
Hello WorldHello World

if文

特定の変数が存在する時にだけ、特定のテンプレートを出力することもできます。

<% if (flag) { %>
    <h2>フラッグが立っている</h2>
<% } %>

この場合はflagに任意の値が設定されているときに

フラッグ

が展開されます。

以下のようにif ~ else文にすることも可能です。

<% if (flag1 || flag2) { %>
    <h2>フラッグ1とフラッグ2が立っている</h2>
<% } else if(flag1 && !flag2) {  %>
    <h2>フラッグ1だけ立っている</h2>
<% } else if(!flag1 && flag2) {  %>
    <h2>フラッグ2だけ立っている</h2>
<% } else { %>
    <h2>フラッグが立っていない</h2>
<% } %>

このように複雑な条件分岐も可能です

flag1とflag2に任意の値が設定されているとき
    <h2>フラッグ1とフラッグ2が立っている</h2>
flag1にだけ任意の値が設定されているとき
<h2>フラッグ1だけ立っている</h2>
flag2にだけ任意の値が設定されているとき
<h2>フラッグ2だけ立っている</h2>
flag1・flag2ともに値が設定されていない
 <h2>フラッグが立っていない</h2>

switch

for文

テンプレートエンジンを利用する一番の利点はfor文を利用して同じ構造でそれぞれ違う値をもった要素を複数配置できることです。

たとえば、以下のような同じ構造をもったオブジェクトの配列に対して

var languages = [
  {"name":"Python","developer":"Guido van Rossum"},
  {"name":"Ruby","developer":"Matsumoto Yukihiro"},
  {"name":"Java", "developer":"James Gosling"},
  {"name":"C++", "developer":"Bjarne Stroustrap"}
];
<table>
  <tr>
     <th>言語名</th>
     <th>開発者</th>
  </tr>
  <% for (var i = 0; i < languages.length; i++) { %>
     <tr>
         <td><%- languages[i].name %></th>
         <td><%- languages[i].developer %></th>
     </tr>
  <% } %>
</table>

としてテーブルを展開することも可能です。

これは長さが定まっていないリスト構造に対して使用すると非常に効果的です。

forEach文

forEach文を利用することも可能です。

<ul>
  <% languages.forEach(function(language) { %>
     <%- include('item', {item: language}) %>
  <% } %>
</ul>

子要素

テンプレート内に任意のほかのテンプレートを展開することも可能です

<% include("child", {item: language}) %>

child.ejs

<p>子要素です</p>

結果

<div>
<p>子要素です</p>
</div>

基本的に変数はそのまま利用できる

<p><%- @title %></p>
<!DOCTYPE html>
<html>

    <head>
        <title> <%- @title %> </title>
    </head>
    
    <body>
           <% include "child" %>
    </body>

</html>

変数を再定義して渡すこともできる。

var title = "Hello "
child.ejs
<p>
<%= title %>
</p>

index.ejs
<!DOCTYPE html>
<html>

    <head>
        <title> <%- @title %> </title>
    </head>
    
    <body>
           <% include "child", {title: title + "hoge"} %>
    </body>

</html>

結果

<!DOCTYPE html>
<html>

    <head>
        <title> <%- @title %> </title>
    </head>

    <body>
       <p>
          Hello hoge
       </p>
    </body>

</html>