Gulp + EJS初心者のつまづきポイントまとめ

# PHPが使えない環境で役立つテンプレートエンジン

筆者が関わっている案件ではほぼPHPが使えるのですが、時々PHPが使えない案件があります。そんな時に共通部分、特に &lt;head&gt;〜&lt;/head&gt; を外部ファイルにしたい時に役立ちました。

まだ使って一週間も経っていませんが、気付いた点をまとめてみました。誤っている点などありましたらご指摘お願いいたします。

# 環境

ここではGulpを使う環境で紹介していきます。
フォルダ構成はこのようになっています。

```
/ ← ここにコンパイルしたものを出力
+ .src/
  + node_modules/
  + ejs/
    + vars/ ← 定数など
    | + _const.ejs
    + module/ ← 使い回しするパーツ
    | + _social.ejs
    + common/ ← ヘッダやフッタ共通パーツ
    | + _header.ejs
    + content_A/ ← 各コンテンツ
    | + content_a.ejs
    + index.ejs ← トップページ
```


## Gulpの設定

下記の他にもSassの設定などありますが、必要な部分だけ書き出しました。

```gulpfile.js
var path = {
  module: '/usr/local/lib/node_modules/',
  src : "",
  dist : "../"
};

// gulp本体
var gulp = require('gulp');
// エラー停止を回避
var plumber = require(path.module + 'gulp-plumber');

// EJS
var ejs_src = [path.src+"ejs/*.ejs", path.src+"ejs/**/*.ejs", "!"+path.src+"ejs/**/_*.ejs"];
var ejs_src_watch = [path.src+"ejs/*.ejs", path.src+"ejs/**/*.ejs"];
var ejs_dist = path.dist+"";
var ejs = require(path.module + 'gulp-ejs');
gulp.task("ejs", function(){
  gulp.src(ejs_src)
    .pipe(plumber())
    .pipe(ejs('',{"ext": ".html"}))
    .pipe(gulp.dest( ejs_dist ));
});

// 更新監視
var watch = require(path.module + 'gulp-watch');
gulp.task("default", function(){
  watch( ejs_src_watch, function(e){ gulp.start("ejs") } );
  // 〜他にも sass、webpack、imagemin、changed など
});

```

### gulpfile.jsの解説
```gulpfile.js
var ejs_src = [path.src+"ejs/*.ejs", path.src+"ejs/**/*.ejs", "!"+path.src+"ejs/**/_*.ejs"];
```

この部分は変換対象ファイルと、除外ファイルの指定です。

`ejs/**/*.ejs`でサブフォルダ以下を対象としています。
`!ejs/**/_*.ejs`は「_（アンダーバー）」で始まるファイルを変換対象から除外しています。変数ファイルや、共通パーツなどです。

Sassのように「_」で始まるファイルを除外してくれませんので、自分で指定する必要があります。

```gulpfile.js
var ejs_src_watch = [path.src+"ejs/*.ejs", path.src+"ejs/**/*.ejs"];
```

更新監視の対象ファイルです。「_」で始まるファイルも監視対象にしたいので分けています。

```gulpfile.js
    .pipe(ejs('',{"ext": ".html"}))
```

拡張子を指定しないと「.ejs」になってしまうので、指定しています。もちろん「.php」として出力することも出来ます。

第一引数はJsonファイルを指定するのに使いますが、使っていないので「''」にしています。

# EJS初心者のハマりどころ

## 参考URL

EJSの構文については大変わかりやすくまとめてくださっている方がいますので、そちらをご覧下さい。

≫[テンプレートエンジンEJSで使える便利な構文まとめ](http://qiita.com/y_hokkey/items/31f1daa6cecb5f4ea4c9)

## グローバル変数

`var`を付けて初期化したものは`<%〜%>`内のローカル変数となります。
付けないで初期化したものはグローバル変数になります。ただしファイル内のみのグローバル変数です（次で説明）。

```sample.ejs
<%
foo = 'global';
var bar = 'local';
%>
foo <= foo >
bar <= bar >
```
```sample.html
foo global
bar ←エラーになる
```

## include()　先には変数を引き継げない

`include()`した先のファイルでは変数を引き継いでくれません。変数のスコープはファイル毎になります。そのため変数を引き継ぎたい場合は`include(ファイル名,変数);`のように受け渡す必要があります。

```index.ejs
<%
const = {
  title: 'ページタイトル',
  description: '説明文'
};
%>
↓拡張子は省略できます。
<%- include('_header', {const:const}); %>
```
```_header.ejs
<meta name="description" content="<%= const.description %>">
<title><%= const.title %></title>
```

## include() の起点は各ファイルから

例えば下記のような構成があるとします。

```
index.ejs
+ common/
| + _header.ejs
+ module/
  + _social.ejs
```

次の例はエラーになります。

```index.ejs
<%- include('common/_header'); %>
```
```common/_header.ejs
<%- include('module/_social'); %>
```
```module/_social.ejs
// なんらかの処理
```

PHPの`include()`の考えだと、一番上のファイル（この場合は`index.ejs`）を起点としますが、EJSの場合は各ファイルの位置を起点とします。

下記のようにすると正しく動作します。

```common/_header.ejs
<% -include('../module/_social'); %>
```

なので、読み込み起点を変数にする必要は全くありません。

## include() 先の関数は使えるの？

使えますが色々制約があるようです。

### 駄目だった例①

`hoge()`がundefinedになります。

```index.ejs
<%
include('test'); 
hoge();
%>
```
```test.ejs
<% function hoge(){ %>
foo
<% } %>
```

### 駄目だった例②

エラーは出ませんが何も表示されません。

```index.ejs
<%
include('test'); 
hoge();
%>
```
```test.ejs
<% hoge = function(){ %>
foo
<% } %>
```

何か表示するには `<%- %>` か、 `<%= %>` を使う必要があります。しかし、それらで囲んだところでエラーになります。

```index.ejs
<%- include('test'); hoge(); %>
```

`<%- %>` 、 `<%= %>`は例えるなら`console.log();`みたいなものなので複数構文は使えません。

### 大丈夫だった例①

`return`で返し、それをグローバル変数で受け取るのは大丈夫でした。

```index.ejs
<%
include('test');
foo = hoge();
%>
<%- foo %>
```
```test.ejs
<% hoge = function(){
  return 'foo';
} %>
```

## include() 先で条件分岐するには

読み込み先の関数が使えないのは不便ですが、`include()`自体がファイル＝関数を実行しているというイメージなので、パラメータを与えてあげれば分岐は出来ます。

```index.ejs
<%- include( '_header', {active:'top'} ); %>
```
```_header.ejs
<% if( active == 'top' ){ %>
トップページ用の処理
<% }else{ %>
それ以外の処理
<% } %>
```

# 気付いたことがあればまた更新します

触って2日も経ってないので間違いがあるかもしれません。
ご指摘お待ちしています。