EJSの基本の使い方と、今後のゆくえ(2020~)


2020年12月19・20日に調べた
EJS(テンプレートエンジン)の使い方と
EJSの今後(2020〜)について考察しました
想定としては、React, Angular, Vue を覚える前のサーバ初心者が
Node + Express を使って
サーバ側でレンダリングを気軽にやってみたいなと思った時に
EJSで手軽に出来るよ、という内容です

2020年12月21日 1回目更新
2020年12月28日 2回目更新

## 目次
1. [始めに](#始めに)
2. [サーバ側の事前準備](#サーバ側の事前準備)
3. [タグ](#タグ)
4. [include()](#include)
4. [EJSの今後](#今後)
5. [まとめ](#まとめ)

## 始めに

この記事で

想定している事

- JavaScript(DOM)はある程度書ける
- HTMLを5〜30分程でレンダリングする
- Node + Express を使ってサーバ側でレンダリングする
- 基本タグ(<%# %>,<% %>,<%= %>,<%- %>)を足すだけなので学習コストが低い
- ローカルサーバ側でのinclude(パス、オブジェクト値)の使い方も簡単に紹介します
- ローカルサーバ側のオブジェクト値が、クライアント側に送れているかを確認する

想定していない事

- 複雑な事や中〜大規模な事は React, Angular, Vue で覚えて下さい
- 複数ではなく、1ページのみのレンダリングを想定しています
- gulpは使いません
  - 代わりに nodemon でレンダリングの自動更新を行います

<br>

``` javascript:環境

ubuntu     18.04
conda      4.8.4
code      1.52.1

node      12.4.0
express   4.16.1
ejs        3.1.5
```

<br>

## サーバ側の事前準備

サーバ環境構築は下記の記事を参考にしてみて下さい

- [Expressjs.com - インストール](http://expressjs.com/ja/starter/installing.html)
- [Expressjs.com - Express でのテンプレート・エンジンの使用](https://expressjs.com/ja/guide/using-template-engines.html)

(node, npm は既にインストールされている事とします)

ターミナルで express nodemon ejs をインストールして

```bash:Terminal

$ mkdir myFirstEJSapp
$ cd myFirstEJSapp
$ npm init -y
$ npm install --save express nodemon ejs
```

index.js ファイルを作り、最低限の設定をします

```javascript:index.js

const express = require('express')　// expressを準備
const app = express()
const port = 3000

// express に ejs のテンプレートエンジンを設定
app.set("view engine", "ejs");

// express に ejs のテンプレートエンジンを設定した場合
// /views フォルダが index.ejs のデフォルトになります

app.get("/", function (req, res) {
  res.render("index"); // これで /views/index.ejs をレンダリング
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})
```

次に、 package.json ファイルに "start": "nodemon ./index.js"
を加えて、 `npm start` で nodemon が起動する様にします

```json:package.json
{
  "name": "myFirstEJSapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon ./index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ejs": "^3.1.5",
    "express": "^4.17.1",
    "nodemon": "^2.0.6"
  }
}
```

次に、 index.ejs ファイルを views フォルダ内に作成します

```html:views/index.ejs
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>myFirstEJSapp</title>
  </head>
  <body>
    <div>
      テスト
    </div>
  </body>
</html>
```

<br>

今の段階で

myFirstEJSapp (root)
--- node_modules
--- views
------ views/index.ejs
--- index.js
--- package-lock.json
--- package.json

といった構成になります

この段階で
ターミナルで `npm start` とやると
nodemon（自動更新）が作動し
index.ejs が、サーバ側からレンダリングされた状態で
http://localhost:3000 にローカルサーバが構築されています

もしローカルサーバを構築するのも大変な人は、
ここまでで疲れてしまう人もいるかもしれませんが
ここまで来れば、ほぼ終わったも同然ですので頑張ってみて下さい

## EJSの使い方

ここからEJSの使い方を紹介していきます

## タグ

この記事では
基本的な4つのタグの使い方を紹介していきます
タグは全て index.ejs, 若しくは他の xxx.ejs 内で使っていく事を想定しています

[<元ネタ>](https://www.npmjs.com/package/ejs#tags)

基本タグ四種
1. `<%# %>` コメント用タグ
2. `<% %>` コード用タグ
3. `<%= %>` 出力用タグ（タグはエスケープ）
4. `<%- %>` 出力用タグ（タグ込）

下記は省略
- <%_
- <%%
- %%>
- %>
- -%>
- _%>

もしローカルサーバを構築出来なかった人・今は手元にない人でも
オンラインでコードをテスト出来る、下記サイトを使ってみて下さい

左側がサーバ
右側がクライアント
になります

https://ionicabizau.github.io/ejs-playground/

### 1. `<%# %>` コメント用タグ

先ず、最初に紹介したいのは
**`<%# %>`** コメント用タグで
HTML(クライアント)側からは見られない
サーバ側だけが読めるコメントになります

これは
javascript で言う所の `// コメント`
HTML で言う所の `<!-- コメント -->`
になります

先ずは下記リンクで

https://ionicabizau.github.io/ejs-playground/

```html:サンプルコード
<%# これはコメント用 %>
```

と試してみて下さい
クライアント側には何も表示されない筈です

index.ejs内(サーバ側)でも
`<%# これはコメント用 %>` が見られますが

```html:index.ejs
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>myFirstEJSapp</title>
  </head>
  <body>
    <div>テスト</div>
    <%# これはコメント用 %>   <--- サーバ側からは見られる
  </body>
</html>
```

ローカルサーバ ( http://localhost:3000 ) の
クライアント側 ( index.html ) からは
何も表示されません

```html:index.html
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>myFirstEJSapp</title>
</head>
  <body>
    <div>テスト</div>
　　   　　　　　　　 　<--- クライアント側からは見られません
  </body>
</html>
```

### 2. `<% %>` コード用タグ

次に紹介したいのが
`<% %>` コード用タグ

通常 HTML の [DOM](https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Introduction) を操作したい場合は
JavaScript のコードを `<script></script>` タグ内にコードを書いていきますが
EJS で HTML をレンダリングしたい時は `<script></script>` タグを使わず
`<% %>` タグを使って JavaScript コードを書いていきます

ここでのポイントは
最終的なゴールは
<% %>内で定義した代数(例： const daisu = "代数")や関数を
<%= %>, <%- %> に出力して
HTML上 (クライアント側) で見られるようにする事です

例えば
下のサンプルコードの様に
先ずは `<% %>` 内で `test_code` という代数を定義して
`<%= %>` 内で　"このコードが見られます"　という出力を出します

```html:サンプルコード
<%# 下記で代数を定義します %>
<% const test_code = "このコードが見られます"  %>
<%= test_code  %>
```

先ずは
下記リンクで上記のコードがどんな風に出力されているか試してみて下さい

https://ionicabizau.github.io/ejs-playground/

次に
index.ejs（サーバ側）で下記の様に書くと

```html:index.ejs
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>myFirstEJSapp</title>
  </head>
  <body>
    <div>テスト</div>
    <%# 下記で代数を定義します %>
    <% const test_code = "このコードが見られます" %>

    <%= test_code %>      <--- ここの内容しかHTML上では見られません
  </body>
</html>
```

HTML上（クライアント側）に出力されているのは
`<%= %>`
内の部分だけになり
他のコードは見られません

```html:index.html
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>myFirstEJSapp</title>
</head>
  <body>
    <div>テスト</div>
    "このコードが見られます"
  </body>
</html>
```

### 3-4. 出力用タグ(`<%= %>` と `<%- %>`)の違い  

`<% %>` タグ内で定義した代数を
`<%= %>` タグ内で出力出来る様になりました

次は
`<%= %>` タグと `<%- %>` タグの違いについて説明していきます

`<%= %>` はタグ内で定義したタグをエスケープして出力する
`<%- %>` はタグ内で定義したタグをエスケープしないで出力する

サンプルコードで説明すると

https://ionicabizau.github.io/ejs-playground/

上記のリンクで、下記のサンプルコードを試すと

```html:サンプルコード
<%# 下記で代数を定義します %>
<% const test_code = "<p>このコードが見られます<p>"  %>
<%= test_code  %>
```

`&lt;p&gt;このコードが見られます&lt;p&gt;`

( `<>` がエスケープされて上記の状態になる)

といった出力になるのに対して

下記のサンプルコードを試すと

```html:サンプルコード
<%# 下記で代数を定義します %>
<% const test_code = "<p>このコードが見られます<p>"  %>
<%- test_code  %>
```

`<p>このコードが見られます<p>`

という風に`<p>`タグや色んなタグを出力出来るようになり
HTMLの見た目をサーバ側から弄ることが出来るようになります

なのでEJS上で
`<>`タグが使いたい場合は<%- %>
オブジェクト値を確認したり、純粋な出力の値だけを確認したい場合は<%= %>
と使い分ける事が出来ます

## include()

include() の使い方を簡単に説明すると、
index.ejs 外で取得した代数値やオブジェクト値を
index.ejs 外のテンプレート (template.ejs) を使って
index.ejs 内で表現したい時に役立ちます

これは例えば
サーバ側で fetch や Twitter API 等を使って
オブジェクト値 (ツイート情報) を取得した時に
index.ejs に Twitter のツイートを表現したい（埋め込んだ様に見せたい）時に
簡単に導入する方法です

構文は

`incldue(パス, オブジェクト値)`

でオブジェクト値は無くても大丈夫です
パスも index.ejs で無くても
ejs を省略して
index だけでもOKです

<br>

例えば下記の様な階層だとします

myFirstEJSapp (root)
--- node_modules
--- views
------ views/index.ejs
------ views/template.ejs
--- index.js
--- package-lock.json
--- package.json

そこで
views/index.ejs が下記の様だったとします

```html:views/views/index.ejs
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>myFirstEJSapp</title>
  </head>
  <body>
    <%- include("./template") %>  <--- template.ejs からテンプレートを取得してきます
  </body>
</html>
```

すると
サーバ側 ( index.js や 自作API ) で fetch や request や Twitter API 等で
オブジェクト値 (ツイート情報) を取得して

```javascript:index.js
const express = require("express"); // expressを準備
const app = express();
const port = 3000;

// express に ejs のテンプレートエンジンを設定
app.set("view engine", "ejs");

// express に ejs のテンプレートエンジンを設定した場合
// /views フォルダが index.ejs のデフォルトになります

// Twitter API でオブジェクト値(ツイート文)を取得したものと想定します
let data = {
  tweet1: "これは、ツイート1です", 
  tweet2: "これは、ツイート2です",
  tweet3: "これは、ツイート3です",
};

// 取得したオブジェクト値 ( data ) を views/index.ejs に送っています
app.get("/", function (req, res) {
  res.render("index", data); // これで /views/index.ejs をレンダリング
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});
```

オブジェクト値や代数値を
自分の使いたいテンプレートページ (template.ejs) に落とし込んで行きます

下記のテンプレートファイル (template.ejs) に
自分が表現したい書き方をして下さい

```html:views/template.ejs
<div>
  <h3>テスト1</h3>
</div>

<%# 下記のオブジェクト値をindex.ejs外から挿入・変更します %>
<%= tweet1 %>   <--- この値はサーバ側(index.js)から送られています
<p></p>

<div>
  <h3>テスト2</h3>
</div>

<%# 下記のオブジェクト値をindex.ejs外から挿入・変更します %>
<%= tweet2 %>   <--- この値はサーバ側(index.js)から送られています
<p></p>

<div>
  <h3>テスト3</h3>
</div>

<%# 下記のオブジェクト値をindex.ejs外から挿入・変更します %>
<%= tweet3 %>   <--- この値はサーバ側(index.js)から送られています
<p></p>
```

こうする事によって
サーバ側から取得したオブジェクト値(ツイート文)を
自分の描きたいテンプレートに代入して
それを下記の様に index.html に表現が出来るようになります

```html:index.html
<body>
    <div>
      <h3>テスト1</h3>
    </div>
    これは、ツイート1です
    <p></p>

    <div>
      <h3>テスト2</h3>
    </div>
    これは、ツイート2です
    <p></p>

    <div>
      <h3>テスト3</h3>
    </div>
    これは、ツイート3です
    <p></p>
</body>
```

## 今後

2020年12月現在
Qiitaサイト内で
EJS ( 又はテンプレートエンジン ) についての記事を調べていると
勢いがあったのは大体2-4年前までの記事な気がしました

下記の英語の質問箱を見ると

[Are template engines still relevant in 2020? - DEV](https://dev.to/rzeczuchy/are-template-engines-still-relevant-in-2020-1hkk)


原因としては
React, Vue, Angular の登場でテンプレートエンジン自体が下火になっている説
フロントエンドの方が人気でバックエンドはあまり人気が無い説
もうテンプレートエンジンの機能をアップデートする必要性があまり無い説
があるそうです

そこで
2020年現在
テンプレートエンジンを使うメリットを考えてみると

- サーバ側での小規模、パイロットプロジェクトをテスト・デバッグが簡単に出来る
- JAM (JavaScript, API, Markup) stack 人気がきている

といった利点があるらしいです

[JAM stackとは (Qiita 記事@ozaki25)](https://qiita.com/ozaki25/items/4075d03278d1fb51cc37)

なので
サーバ初心者、特にレンダリングが良く分からない
React, Angular, Vue を触る前の

サーバ側からどうやってHTMLをレンダリングしているか、試しにいじってみたい人用に
Single Page Application (SPA) 用の１ページだけのアプリを簡単に作って見たい人用に
ちょっと小規模テスト・デバッグ用に使いたい人用に
EJS、他テンプレートエンジンは使い勝手が良いのかな？
と思いました

[Qiita - SPAの基本](https://qiita.com/takanorip/items/82f0c70ebc81e9246c7a)

## まとめ

まとめると
現在、EJSの使用利点は段々と限られてきていて（React等のフレームワークに代替してきて）
主力としてのレンダリングツールとしてはテンプレートエンジンの役目ではなくなってきています

ですが
<%= %>,<%- %>出力タグを使って、外部APIの値が取得出来てるかどうかをHTML上で確認したり
include(パス, オブジェクト値)を使ってテンプレ表現をちゃんと実装出来るかどうかテストしてみる
といった小規模のミニプロジェクトを軽く試してみたい場合は
EJSは学習コスト・レンダリングコストが低く
5〜30分ぐらいで大体実装出来てしまいます

そんなEJS活用方法は
React, Angular, Vue 触る前のサーバ初心者には
重宝するのではないでしょうか？


## Qiita内トップEJS記事まとめ(2020)

３年以内

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

@y_hokkey
2017年02月02日に更新

[EJSの基本的な書き方 - Qiita](https://qiita.com/miwashutaro0611/items/36910f2d784ff70a527d)

@miwashutaro0611
2018年06月23日

[Expressにおけるejsの使い方 - Qiita](https://qiita.com/kamihork/items/1b13d2157979d1837849)

@kamihork
2018年10月07日に更新

---

３年以上前

[gulpで手軽にEJSテンプレートをHTMLに変換 - Qiita](https://qiita.com/yuichiroharai/items/63b0769bc8ebe0220018)

@yuichiroharai
2014年10月15日に更新

[ejsでextendっぽいことをする方法 - Qiita](https://qiita.com/fnobi/items/6225673eb52c698844eb)

@fnobi
2013年07月25日

[EJS+gulpで静的サイトのHTML生成 - Qiita](https://qiita.com/oden/items/b46abf996d8e066d968f)

@oden
2016年03月15日

<br>

<a id="top_t_engine"></a>

## TOP テンプレートエンジン (2020)

"JavaScript template engine 2020"とググった時に出てきた  
人気 JavaScript テンプレートエンジン 6 (2020)  

. | Pug (Jade) | Handlebars | Mustache | doT | EJS | Nunjucks
--- | --- | --- | --- | --- | --- | ---
記事数 | 11 | 10 | 9 | 9 | 8 | 8
npm | [LINK](https://www.npmjs.com/package/pug) | [LINK](https://www.npmjs.com/package/handlebars) | [LINK](https://www.npmjs.com/package/mustache) | [LINK](https://www.npmjs.com/package/dot) | [LINK](https://www.npmjs.com/package/ejs) | [LINK](https://www.npmjs.com/package/nunjucks)
github | [LINK](https://github.com/pugjs/pug) | [LINK](https://github.com/handlebars-lang/handlebars.js) | [LINK](https://github.com/janl/mustache.js) | [LINK](https://github.com/olado/doT) | [LINK](https://github.com/mde/ejs) | [LINK](https://github.com/mozilla/nunjucks)
Licenses | MIT | MIT | MIT | MIT | Apache-2.0 | BSD-2-Clause
Dependecies | 8 | 5 | 0 | 0 | 1 | 4
Weekly Downloads | 1,121,333 | 7,265,726 | 1,933,955 | 240,792 | 8,145,826 | 294,753
Unpacked Size | 59.7 kB | 2.72 MB | 113 kB | 73.8 kB | 134 kB | 1.74 MB
npm内: 人気 | 61% | 67% | 60% | 33% | 80% | 47%
npm内: 質 | 63% | 63% | 63% | 75% | 62% | 93%
npm内: メンテ | 28% | 33% | 33% | 47% | 33% | 33%
npm collaborator | 2 | 5 | 3 | 2 | 1 | 5
github star | 19.8k | 15.7k | 14.3k | 4.6k | 5.1k | 7.1k
github used by | 286k | 3m | 215 | 14.1k | - | 121k
github contributor | 248 | 175 | 110 | 13 | 113 | 137

ソース記事

- [Social Network for Programmers and Developers](https://morioh.com/p/501112d590d1)
- [31 Best JavaScript templating engines as of 2020 - Slant](https://www.slant.co/topics/51/~best-javascript-templating-engines)
- [Openbase](https://openbase.io/categories/js/best-nodejs-html-templating-engine-libraries?orderBy=RECOMMENDED&)
- [7 JavaScript Templating Engines with Code Examples - Developer Drive](https://www.developerdrive.com/best-javascript-templating-engines/)
- [The best open source javascript template engines \| Web Design and Web Development news, javascript, angular, react, vue, php](https://www.ma-no.org/en/programming/javascript/the-best-open-source-javascript-template-engines)
- [What good template language is supported in JavaScript?](https://www.xspdf.com/resolution/128949.html)
- [10 Best Open-source JavaScript template engine ( 2019 )](https://www.dunebook.com/best-javascript-template-engine/)
- [Templating engines](https://jsreport.net/learn/templating-engines)
- [GitHub - marko-js/templating-benchmarks: Benchmarking framework for multiple templating engines](https://github.com/marko-js/templating-benchmarks)
- [Template Engines in Node JS with Express \| EJS Vs Nunjucks](https://tutorial.techaltum.com/template-engine.html)
- [15 JavaScript Template Engines for Front-end Development](https://codecondo.com/15-javascript-template-engines/)
- [Intro to JavaScript Template Engines](https://www.htmlgoodies.com/beyond/javascript/js-ref/intro-to-javascript-template-engines.html)