タイトル: テンプレートエンジン
SEOタイトル: JavaScriptテンプレートエンジン完全比較(EJS/Handlebars/Pug/JSX/SSR)
| この記事の要点 |
|
テンプレートエンジンとは
テンプレートエンジンは、HTML(あるいはメール本文、JSON、ファイル名)の雛形に変数や制御構文を埋め込んで、動的にレンダリングするライブラリです。サーバサイドの SSR、メール送信、コード生成、CMS、CLI スカフォルドなど用途は多岐に渡ります。
主要 JavaScript テンプレートエンジン比較
| エンジン | 構文タイプ | ロジック | エスケープ | 主用途 |
|---|---|---|---|---|
| EJS | <% %> 埋め込み JS | フルJS可 | 自動 (<%= %>) | Express SSR |
| Handlebars | {{ }} ロジックレス | ヘルパー経由のみ | 自動 | メール / 静的サイト |
| Pug (旧 Jade) | インデント記法 | 制限付き | 自動 | SSR / モックアップ |
| Mustache | ロジックレス {{ }} | 条件・ループのみ | 自動 | 多言語移植性重視 |
| Nunjucks | Jinja2 風 {% %} | フィルタ/マクロ | 自動 | 静的サイト (11ty) |
| Liquid | Shopify 由来 | 制限付き | 自動 | Shopify / Jekyll |
| JSX (React) | HTML in JS | フルJS | 自動 | SPA / SSR |
| Vue Template | HTML + ディレクティブ | 制限付き表現 | 自動 | SPA / SSR |
| Svelte | HTML + JS ブロック | フルJS | 自動 | SPA / SSR |
EJS(最も Express 的)
<%= title %>
<% items.forEach((it) => { %>
- <%= it.name %>
<% }) %>
Handlebars(ロジックレス)
{{title}}
{{#if user}}
Welcome, {{user.name}}
{{else}}
Please log in
{{/if}}
{{#each items}}
- {{this.name}}
{{/each}}
{{!-- ヘルパー登録 --}}
{{formatDate created_at "YYYY-MM-DD"}}const Handlebars = require('handlebars');
Handlebars.registerHelper('formatDate', (date, fmt) => {
return dayjs(date).format(fmt);
});
const tmpl = Handlebars.compile(source);
const html = tmpl({ title: 'My Site', items: [...] });
Pug(インデント記法)
doctype html
html
head
title= title
body
h1 Welcome, #{user.name}
ul
each item in items
li= item.name
if user.isAdmin
a(href="/admin") Admin Panel
Mustache(最小・移植性重視)
{{title}}
{{#user}}
Welcome, {{name}}
{{/user}}
{{^user}}
Please log in
{{/user}}
{{#items}}
- {{name}}
{{/items}}
Mustache はロジックを意図的に制限しているため、多くの言語に同名実装(Python・Ruby・PHP・Java)があるのが強み。多言語システムでテンプレート共有したいとき強力。
Nunjucks(Jinja2 風)
{{ title }}
{% if user %}
Welcome, {{ user.name | upper }}
{% endif %}
{% for item in items %}
{{ loop.index }}. {{ item.name }}
{% endfor %}
{% macro card(item) %}
{{ item.name }}
{% endmacro %}
{{ card(items[0]) }}
JSX / Vue / Svelte(モダン UI フレームワーク)
// React JSX
function Page({ title, items }) {
return (
{title}
{items.map(it => - {it.name}
)}
);
}
// Vue SFC
{{ title }}
- {{ it.name }}
// Svelte
{title}
{#each items as it (it.id)}
- {it.name}
{/each}
テンプレートリテラル(標準 JS)
外部ライブラリ無しで動く最小構成。ちょっとした文字列組み立てには十分です。
const name = 'Alice';
const html = `Hello, ${name}!
`;
// タグ付きテンプレートで自動エスケープも自作可能
function html(strings, ...values) {
const escape = s => String(s)
.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
return strings.reduce((acc, str, i) => {
return acc + str + (i < values.length ? escape(values[i]) : '');
}, '');
}
const safe = html`${userInput}
`; // 自動エスケープ
選定の指針
| 要件 | 推奨 |
|---|---|
| Express で SSR、HTML をそのまま書く | EJS |
| メール本文、テンプレートの設計者は非エンジニア | Handlebars / Mustache |
| インデントで省タイプ、モック作成 | Pug |
| 静的サイトジェネレータ | Nunjucks (11ty) / Liquid (Jekyll) |
| SPA / インタラクティブ UI | React (JSX) / Vue / Svelte |
| SSR + SPA ハイブリッド | Next.js / Nuxt / SvelteKit |
| ごく単純な文字列組み立て | テンプレートリテラル |
XSS と自動エスケープ
ユーザ入力をテンプレートに埋め込むときは自動 HTML エスケープが必須です:
入力:
自動エスケープあり:
<script>alert(1)</script> ← 文字として表示(安全)
自動エスケープなし:
← 実行される(XSS)
- EJS:
<%= %>は自動、<%- %>は生 - Handlebars:
{{ }}は自動、{{{ }}}は生 - JSX: 文字列は自動エスケープ、
dangerouslySetInnerHTMLは生 - Vue:
{{ }}は自動、v-htmlは生
SSR との関係
SSR(Server Side Rendering)は本質的にテンプレートレンダリングです:
- Express + EJS / Pug = 古典的 SSR、静的 HTML を返す
- Next.js (React SSR) = JSX をサーバで HTML 化 + クライアントで hydrate
- Nuxt (Vue SSR) = Vue Template を同様に
- SvelteKit = Svelte Component を同様に
FAQ
Q: テンプレートエンジンと React / Vue の違いは?
A: 用途が違います。テンプレートエンジンは静的 HTML 生成に強く、React/Vue はクライアント側の動的 UIに強い。両方を併用する構成も普通です。
Q: テンプレートエンジンは時代遅れ?
A: いいえ。メール本文 / SSR / 静的サイト / コード生成 / CMS で現役。SPA だけが全てではありません。
Q: ロジックレス(Mustache 等)と高機能(EJS 等)どちらが良い?
A: 大規模・複数人ならロジックレスがメンテしやすい。個人や小規模なら EJS の柔軟さが速い。