タイトル: テンプレートエンジン
SEOタイトル: テンプレートエンジン完全ガイド
| この記事の要点 |
|
テンプレートエンジンとは
テンプレートエンジン (Template Engine) は、HTML のひな形 (テンプレート) に動的なデータを差し込み、最終的な HTML を生成するソフトウェアです。例えば「ユーザー名」だけが違う 1000 通りのページを、1 個のテンプレートと 1000 件のデータから生成できます。
<!-- テンプレート (Blade) -->
<h1>こんにちは、{{ $user->name }}さん</h1>
<p>あなたの注文は {{ $orders->count() }} 件です。</p>
@foreach ($orders as $order)
<li>{{ $order->title }} - {{ $order->price }}円</li>
@endforeach
テンプレートエンジンを使わずに「素の PHP」で書くと <?php echo $user->name; ?> のように冗長になり、XSS 脆弱性を生みやすくなります。
テンプレートエンジンの利点
- HTML エスケープの自動化:
{{ }}で出力すると自動でエスケープされ XSS を防ぐ - ロジックとビューの分離: コントローラはデータ取得、テンプレートは表示に集中
- レイアウト継承: 共通レイアウト (ヘッダ / フッタ) を 1 箇所で管理
- コンパイル キャッシュ: テンプレートを最適化された PHP/Python/Java コードに事前変換
- カスタムタグ / フィルタ: 日付フォーマット、通貨表示などを再利用可能に
- セキュリティ: 自動エスケープ + サンドボックスで安全
PHP のテンプレートエンジン
Blade (Laravel 標準)
{{-- layouts/app.blade.php --}}
<!DOCTYPE html>
<html>
<head><title>@yield('title')</title></head>
<body>
<nav>@include('partials.nav')</nav>
<main>@yield('content')</main>
</body>
</html>
{{-- pages/article.blade.php --}}
@extends('layouts.app')
@section('title', $article->title)
@section('content')
<h1>{{ $article->title }}</h1> {{-- 自動エスケープ --}}
{!! $article->html_content !!} {{-- 生 HTML --}}
@if ($user)
<p>ようこそ {{ $user->name }} さん</p>
@else
<a href="/login">ログイン</a>
@endif
@foreach ($comments as $comment)
<article>{{ $comment->body }}</article>
@endforeach
@endsection
Twig (Symfony / 多言語)
{# layout.html.twig #}
<!DOCTYPE html>
<html>
<head><title>{% block title %}{% endblock %}</title></head>
<body>{% block content %}{% endblock %}</body>
</html>
{# article.html.twig #}
{% extends "layout.html.twig" %}
{% block title %}{{ article.title }}{% endblock %}
{% block content %}
<h1>{{ article.title }}</h1>
{{ article.body|raw }} {# 生 HTML #}
{{ now|date('Y-m-d') }} {# フィルタ #}
{% if user %}
<p>{{ user.name }}</p>
{% endif %}
{% for comment in comments %}
<p>{{ comment.body }}</p>
{% endfor %}
{% endblock %}
Smarty (老舗 PHP)
2001 年から続く老舗。EC サイト (EC-CUBE 等) で今も使われています。
Python のテンプレートエンジン
Jinja2 (Flask 標準、Ansible でも使われる)
{# base.html #}
<!DOCTYPE html>
<html>
<body>{% block content %}{% endblock %}</body>
</html>
{# article.html #}
{% extends "base.html" %}
{% block content %}
<h1>{{ article.title }}</h1>
{{ article.body | safe }} {# raw HTML #}
{% if user %}
<p>{{ user.name | upper }}</p>
{% endif %}
<ul>
{% for c in comments %}
<li>{{ c.body }}</li>
{% else %}
<li>コメント無し</li>
{% endfor %}
</ul>
{% endblock %}
Django Templates
Django 標準。Jinja2 とほぼ同じ記法 (実は Jinja2 が Django Templates にインスパイアされた)。
Java のテンプレートエンジン
Thymeleaf (Spring Boot 標準)
HTML 属性として記述するので、テンプレート単体でブラウザで開いても表示が崩れないのが特徴。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1 th:text="${article.title}">記事タイトル</h1>
<div th:if="${user != null}">
<p th:text="|ようこそ ${user.name} さん|"></p>
</div>
<ul>
<li th:each="comment : ${comments}" th:text="${comment.body}"></li>
</ul>
<a th:href="@{/article/{id}(id=${article.id})}">詳細</a>
</body>
</html>
FreeMarker
Apache 系。柔軟性が高く、メール本文生成などにも使われます。
Node.js のテンプレートエンジン
| エンジン | 特徴 | 例 |
|---|---|---|
| EJS | HTML に近い、PHP に似た記法 | <%= title %> |
| Pug (旧 Jade) | インデントベース、簡潔 | h1= title |
| Handlebars | ロジックレス指向、Mustache 拡張 | {{title}} |
| Mustache | 言語非依存、ロジックレス | {{title}} |
| Nunjucks | Mozilla 製、Jinja2 ライク | {{ title }} |
// EJS の例
const ejs = require('ejs');
const html = ejs.render(`
<h1><%= article.title %></h1>
<% if (user) { %>
<p><%= user.name %></p>
<% } %>
`, { article, user });
// Handlebars の例
const Handlebars = require('handlebars');
const template = Handlebars.compile(`
<h1>{{ article.title }}</h1>
{{#if user}}
<p>{{ user.name }}</p>
{{/if}}
{{#each comments}}
<p>{{ this.body }}</p>
{{/each}}
`);
const html2 = template({ article, user, comments });
クライアントサイド (Vue / React / Svelte)
SPA (Single Page Application) ではフロント側にもテンプレート機構があります。
<!-- Vue.js -->
<template>
<h1>{{ article.title }}</h1>
<p v-if="user">ようこそ {{ user.name }}</p>
<ul>
<li v-for="c in comments" :key="c.id">{{ c.body }}</li>
</ul>
</template>
<script setup>
import { ref } from 'vue';
const article = ref({ title: '...' });
</script>// React JSX
function Article({ article, user, comments }) {
return (
<>
<h1>{article.title}</h1>
{user && <p>ようこそ {user.name}</p>}
<ul>
{comments.map(c => <li key={c.id}>{c.body}</li>)}
</ul>
</>
);
}
静的サイトジェネレータ (SSG)
| ツール | 言語 | テンプレート | 用途 |
|---|---|---|---|
| Hugo | Go | Go templates | 高速ブログ、ドキュメント |
| Eleventy (11ty) | Node.js | Nunjucks/Liquid/EJS/Markdown | 軽量ブログ |
| Jekyll | Ruby | Liquid | GitHub Pages |
| Gatsby | React | JSX + GraphQL | React 派生 |
| Next.js (SSG モード) | React | JSX | React + ISR |
| Astro | 多言語 | 独自 + Vue/React/Svelte 混在 | パフォーマンス重視ブログ |
| VitePress / Docusaurus | Vue / React | 各々 | ドキュメント |
Plain PHP vs テンプレートエンジン
<?php // Plain PHP - 動くが推奨されない ?>
<h1><?= htmlspecialchars($title) ?></h1>
<?php if ($user): ?>
<p>こんにちは <?= htmlspecialchars($user->name) ?></p>
<?php endif; ?>
<ul>
<?php foreach ($items as $item): ?>
<li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>
</ul>
<?php // Blade - 推奨 ?>
<h1>{{ $title }}</h1>
@if ($user)
<p>こんにちは {{ $user->name }}</p>
@endif
<ul>
@foreach ($items as $item)
<li>{{ $item }}</li>
@endforeach
</ul>
コンパイル キャッシュの仕組み
多くのテンプレートエンジンは、テンプレートを1 度だけパースして最適化された PHP/Java/JS コードに変換し、ファイルとしてキャッシュします。2 度目以降はキャッシュを読むだけなので高速です。
Laravel Blade のキャッシュ場所:
storage/framework/views/*.php
Twig のキャッシュ:
var/cache/dev/twig/
Thymeleaf:
メモリ上にコンパイル済テンプレート保持
# 開発時はキャッシュ無効化、本番では有効化
# Laravel: APP_DEBUG=false で自動キャッシュ
選定ガイド
| 状況 | 推奨 |
|---|---|
| Laravel プロジェクト | Blade (標準で十分強力) |
| Symfony / 純 PHP | Twig |
| Spring Boot | Thymeleaf (公式チュートリアル基準) |
| Django | Django Templates (標準) |
| Flask | Jinja2 (標準) |
| Express.js | EJS / Pug、両方人気 |
| 静的サイト (高速重視) | Hugo |
| 静的サイト (柔軟性重視) | Astro / Eleventy |
| SPA | React JSX / Vue Template / Svelte |
FAQ
Q: テンプレートエンジンを使わず素の PHP/Node でも書けるが、なぜ使う?
A: 自動エスケープ、レイアウト継承、可読性、保守性、IDE 補完、コンパイルキャッシュ、開発者間の共通認識。長期保守を考えれば導入する価値があります。
Q: ロジックをテンプレート側に書いてもよい?
A: 表示に関する分岐 (if/for) は OK。ビジネスロジック (DB 操作、計算) はコントローラやサービス層に置くべき。「テンプレートは表示専用」を意識します。
Q: パフォーマンスはどれが速い?
A: コンパイルキャッシュが効けば差は僅かです。Hugo (Go) はビルド速度が非常に速いことで有名。サーバ側は 適切なキャッシュ設定が最重要で、エンジン選定の差は通常無視できます。