この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:3
ページ更新者:T
更新日時:2026-06-11 07:07:02

タイトル: テンプレートエンジン
SEOタイトル: JavaScriptテンプレートエンジン完全比較(EJS/Handlebars/Pug/JSX/SSR)

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>テンプレートエンジン</strong> = HTML 等の雛形に変数や制御構文を埋め込んでレンダリングする仕組み</li><li>JS 主要エンジン: <strong>EJS / Handlebars / Pug / Mustache / Nunjucks / Liquid</strong></li><li>モダン UI: React の <strong>JSX</strong> / Vue・Svelte の<strong>テンプレート構文</strong>もテンプレートエンジンの一種</li><li>シンプル用途は<strong>テンプレートリテラル</strong> <code>`Hello ${name}`</code> で十分(ただしエスケープ自前)</li><li>選定基準: ロジック許容度 / エスケープ自動化 / SSR 対応 / コミュニティ規模 / 既存スタック互換</li><li>XSS 対策: <strong>HTML エスケープを自動化</strong>するエンジンが安全。生 HTML 埋め込みは要慎重</li><li>SSR フレームワーク(Next.js / Nuxt / SvelteKit)は内部で JSX / template compiler を持ち、ハイブリッドレンダリングを実現</li></ul></td></tr></table>
<h2>テンプレートエンジンとは</h2>

<p>テンプレートエンジンは、HTML(あるいはメール本文、JSON、ファイル名)の雛形に<strong>変数</strong>や<strong>制御構文</strong>を埋め込んで、動的にレンダリングするライブラリです。サーバサイドの SSR、メール送信、コード生成、CMS、CLI スカフォルドなど用途は多岐に渡ります。</p>

<h2>主要 JavaScript テンプレートエンジン比較</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>エンジン</th><th>構文タイプ</th><th>ロジック</th><th>エスケープ</th><th>主用途</th></tr></thead>
<tbody>
<tr><td><strong>EJS</strong></td><td><code><% %></code> 埋め込み JS</td><td>フルJS可</td><td>自動 (<code><%= %></code>)</td><td>Express SSR</td></tr>
<tr><td><strong>Handlebars</strong></td><td><code>{{ }}</code> ロジックレス</td><td>ヘルパー経由のみ</td><td>自動</td><td>メール / 静的サイト</td></tr>
<tr><td><strong>Pug (旧 Jade)</strong></td><td>インデント記法</td><td>制限付き</td><td>自動</td><td>SSR / モックアップ</td></tr>
<tr><td><strong>Mustache</strong></td><td>ロジックレス <code>{{ }}</code></td><td>条件・ループのみ</td><td>自動</td><td>多言語移植性重視</td></tr>
<tr><td><strong>Nunjucks</strong></td><td>Jinja2 風 <code>{% %}</code></td><td>フィルタ/マクロ</td><td>自動</td><td>静的サイト (11ty)</td></tr>
<tr><td><strong>Liquid</strong></td><td>Shopify 由来</td><td>制限付き</td><td>自動</td><td>Shopify / Jekyll</td></tr>
<tr><td><strong>JSX</strong> (React)</td><td>HTML in JS</td><td>フルJS</td><td>自動</td><td>SPA / SSR</td></tr>
<tr><td><strong>Vue Template</strong></td><td>HTML + ディレクティブ</td><td>制限付き表現</td><td>自動</td><td>SPA / SSR</td></tr>
<tr><td><strong>Svelte</strong></td><td>HTML + JS ブロック</td><td>フルJS</td><td>自動</td><td>SPA / SSR</td></tr>
</tbody>
</table>

<h2>EJS(最も Express 的)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><h1><%= title %></h1>
<ul>
  <% items.forEach((it) => { %>
    <li><%= it.name %></li>
  <% }) %>
</ul></code></pre>
<h2>Handlebars(ロジックレス)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><h1>{{title}}</h1>
{{#if user}}
  <p>Welcome, {{user.name}}</p>
{{else}}
  <p>Please log in</p>
{{/if}}

<ul>
  {{#each items}}
    <li>{{this.name}}</li>
  {{/each}}
</ul>

{{!-- ヘルパー登録 --}}
{{formatDate created_at "YYYY-MM-DD"}}</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const Handlebars = require(&apos;handlebars&apos;);
Handlebars.registerHelper(&apos;formatDate&apos;, (date, fmt) => {
  return dayjs(date).format(fmt);
});

const tmpl = Handlebars.compile(source);
const html = tmpl({ title: &apos;My Site&apos;, items: [...] });</code></pre>
<h2>Pug(インデント記法)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">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</code></pre>
<h2>Mustache(最小・移植性重視)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><h1>{{title}}</h1>
{{#user}}
  <p>Welcome, {{name}}</p>
{{/user}}
{{^user}}
  <p>Please log in</p>
{{/user}}

<ul>
  {{#items}}
    <li>{{name}}</li>
  {{/items}}
</ul></code></pre>
<p>Mustache はロジックを意図的に制限しているため、<strong>多くの言語に同名実装</strong>(Python・Ruby・PHP・Java)があるのが強み。多言語システムでテンプレート共有したいとき強力。</p>

<h2>Nunjucks(Jinja2 風)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><h1>{{ title }}</h1>
{% if user %}
  <p>Welcome, {{ user.name | upper }}</p>
{% endif %}

{% for item in items %}
  <li>{{ loop.index }}. {{ item.name }}</li>
{% endfor %}

{% macro card(item) %}
  <div class="card"><h3>{{ item.name }}</h3></div>
{% endmacro %}
{{ card(items[0]) }}</code></pre>
<h2>JSX / Vue / Svelte(モダン UI フレームワーク)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-jsx">// React JSX
function Page({ title, items }) {
  return (
    <main>
      <h1>{title}</h1>
      <ul>
        {items.map(it => <li key={it.id}>{it.name}</li>)}
      </ul>
    </main>
  );
}

// Vue SFC
<template>
  <main>
    <h1>{{ title }}</h1>
    <ul>
      <li v-for="it in items" :key="it.id">{{ it.name }}</li>
    </ul>
  </main>
</template>

// Svelte
<main>
  <h1>{title}</h1>
  <ul>
    {#each items as it (it.id)}
      <li>{it.name}</li>
    {/each}
  </ul>
</main></code></pre>
<h2>テンプレートリテラル(標準 JS)</h2>

<p>外部ライブラリ無しで動く最小構成。ちょっとした文字列組み立てには十分です。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const name = &apos;Alice&apos;;
const html = `<h1>Hello, ${name}!</h1>`;

// タグ付きテンプレートで自動エスケープも自作可能
function html(strings, ...values) {
  const escape = s => String(s)
    .replace(/&/g, &apos;&amp;&apos;).replace(/</g, &apos;&lt;&apos;)
    .replace(/>/g, &apos;&gt;&apos;).replace(/"/g, &apos;&quot;&apos;);
  return strings.reduce((acc, str, i) => {
    return acc + str + (i < values.length ? escape(values[i]) : &apos;&apos;);
  }, &apos;&apos;);
}

const safe = html`<p>${userInput}</p>`;  // 自動エスケープ</code></pre>
<h2>選定の指針</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e8f5e9"><th>要件</th><th>推奨</th></tr></thead>
<tbody>
<tr><td>Express で SSR、HTML をそのまま書く</td><td>EJS</td></tr>
<tr><td>メール本文、テンプレートの設計者は非エンジニア</td><td>Handlebars / Mustache</td></tr>
<tr><td>インデントで省タイプ、モック作成</td><td>Pug</td></tr>
<tr><td>静的サイトジェネレータ</td><td>Nunjucks (11ty) / Liquid (Jekyll)</td></tr>
<tr><td>SPA / インタラクティブ UI</td><td>React (JSX) / Vue / Svelte</td></tr>
<tr><td>SSR + SPA ハイブリッド</td><td>Next.js / Nuxt / SvelteKit</td></tr>
<tr><td>ごく単純な文字列組み立て</td><td>テンプレートリテラル</td></tr>
</tbody>
</table>

<h2>XSS と自動エスケープ</h2>

<p>ユーザ入力をテンプレートに埋め込むときは<strong>自動 HTML エスケープ</strong>が必須です:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">入力: <script>alert(1)</script>

自動エスケープあり:
  &lt;script&gt;alert(1)&lt;/script&gt;  ← 文字として表示(安全)

自動エスケープなし:
  <script>alert(1)</script>          ← 実行される(XSS)</code></pre>
<ul>
<li>EJS: <code><%= %></code> は自動、<code><%- %></code> は生</li>
<li>Handlebars: <code>{{ }}</code> は自動、<code>{{{ }}}</code> は生</li>
<li>JSX: 文字列は自動エスケープ、<code>dangerouslySetInnerHTML</code> は生</li>
<li>Vue: <code>{{ }}</code> は自動、<code>v-html</code> は生</li>
</ul>

<h2>SSR との関係</h2>

<p>SSR(Server Side Rendering)は本質的にテンプレートレンダリングです:</p>

<ul>
<li><strong>Express + EJS / Pug</strong> = 古典的 SSR、静的 HTML を返す</li>
<li><strong>Next.js (React SSR)</strong> = JSX をサーバで HTML 化 + クライアントで hydrate</li>
<li><strong>Nuxt (Vue SSR)</strong> = Vue Template を同様に</li>
<li><strong>SvelteKit</strong> = Svelte Component を同様に</li>
</ul>

<h2>FAQ</h2>

<p><strong>Q: テンプレートエンジンと React / Vue の違いは?</strong><br />
A: 用途が違います。テンプレートエンジンは<strong>静的 HTML 生成</strong>に強く、React/Vue は<strong>クライアント側の動的 UI</strong>に強い。両方を併用する構成も普通です。</p>

<p><strong>Q: テンプレートエンジンは時代遅れ?</strong><br />
A: いいえ。メール本文 / SSR / 静的サイト / コード生成 / CMS で現役。SPA だけが全てではありません。</p>

<p><strong>Q: ロジックレス(Mustache 等)と高機能(EJS 等)どちらが良い?</strong><br />
A: 大規模・複数人なら<strong>ロジックレス</strong>がメンテしやすい。個人や小規模なら EJS の柔軟さが速い。</p>