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

タイトル: テンプレートの共通化(組み込み)
SEOタイトル: EJS テンプレートの共通化 (include) 完全ガイド

この本文には実行可能なタグ等が含まれるため、安全のため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>EJS で部品共通化は <strong><code><%- include('partials/header') %></code></strong> を使用</li><li>変数を渡すには <strong><code><%- include('item', { name: 'foo' }) %></code></strong></li><li><strong>パスは現在のファイルから相対</strong>、<code>views/</code> 配下のディレクトリ構造を反映</li><li><code><%- %></code>(エスケープなし)と <code><%= %></code>(エスケープあり)の使い分けに注意</li><li>レイアウト共通化は <strong><code>express-ejs-layouts</code></strong> で <code>layout.ejs</code> 自動適用</li></ul></td></tr></table>
<h2>EJS の include 構文</h2>

<p>EJS (Embedded JavaScript Templates) は、Node.js / Express で広く使われるテンプレートエンジンです。ヘッダー・フッター・サイドバーなどの<strong>共通パーツを別ファイルに切り出す</strong>には <code>include</code> 関数を使います。</p>
<p><img alt="EJS partials ディレクトリ構造" src="/uploadImage/20181215232849_7162759.png" style="max-width:640px;width:100%;height:auto;border:1px solid #ccc;border-radius:4px"></p>
<h2>基本的な共通化</h2>

<h3>ディレクトリ構成例</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">views/
├── partials/
│   ├── header.ejs       # ヘッダー
│   ├── footer.ejs       # フッター
│   ├── nav.ejs          # ナビゲーション
│   └── meta.ejs         # メタタグ
├── pages/
│   ├── index.ejs
│   └── about.ejs
└── layout.ejs</code></pre>
<h3>views/partials/header.ejs</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title><%= title %></title>
  <link rel="stylesheet" href="/css/style.css">
</head>
<body>
  <header>
    <h1>マイサイト</h1>
    <nav>
      <a href="/">Home</a>
      <a href="/about">About</a>
    </nav>
  </header>
  <main></code></pre>
<h3>views/partials/footer.ejs</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">  </main>
  <footer>
    <p>&copy; 2026 My Company</p>
  </footer>
</body>
</html></code></pre>
<h3>views/pages/index.ejs</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><%- include(&apos;../partials/header&apos;, { title: &apos;Top page&apos; }) %>

<h2>Welcome</h2>
<p>これがトップページです。</p>

<%- include(&apos;../partials/footer&apos;) %></code></pre>
<h2><code><%- %></code> と <code><%= %></code> の違い</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></tr></thead>
<tbody>
<tr><td><code><%= var %></code></td><td>HTML エスケープしてから出力</td><td>ユーザー入力等の表示(XSS 防止)</td></tr>
<tr><td><code><%- var %></code></td><td>エスケープなしでそのまま出力</td><td>HTML 断片 / include 結果</td></tr>
<tr><td><code><% code %></code></td><td>JS 実行のみ、出力なし</td><td>if / for / 変数定義</td></tr>
<tr><td><code><%# %></code></td><td>コメント</td><td>テンプレート内コメント</td></tr>
</tbody>
</table>

<p><strong>include は必ず <code><%- %></code></strong>。<code><%= %></code> で書くと HTML がエスケープされて <code>&lt;div&gt;</code> のように画面に出てしまいます。</p>

<h2>include に変数を渡す</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- views/partials/item.ejs -->
<article class="item">
  <h3><%= name %></h3>
  <p>価格: <%= price %>円</p>
</article>

<!-- 呼び出し側 -->
<% const items = [
  { name: &apos;りんご&apos;, price: 100 },
  { name: &apos;みかん&apos;, price: 80 }
]; %>

<% items.forEach(item => { %>
  <%- include(&apos;partials/item&apos;, { name: item.name, price: item.price }) %>
<% }); %></code></pre>
<p>親テンプレートで定義された変数は<strong>子テンプレートでもそのまま参照可能</strong>(EJS 3 系のスコープ仕様)。第 2 引数で<strong>明示的に渡すと上書き</strong>できます。</p>

<h2>パス解決のルール</h2>

<p>EJS 3 系で <code>include</code> のパスは<strong>現在のファイルからの相対パス</strong>です(EJS 2 系の views ルート相対と異なる)。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">views/pages/index.ejs から
  - &apos;../partials/header&apos;   → views/partials/header.ejs
  - &apos;./sub&apos;                → views/pages/sub.ejs

EJS は拡張子を自動で補完:
  - &apos;header&apos; → &apos;header.ejs&apos;
  - &apos;header.html&apos; → そのまま &apos;header.html&apos;

絶対パス (/ 始まり) は views ルート扱い
  - &apos;/partials/header&apos; → views/partials/header.ejs</code></pre>
<h2>Express でのセットアップ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// app.js
const express = require("express");
const path = require("path");

const app = express();

app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

app.get("/", (req, res) => {
  res.render("pages/index", {
    title: "Top page",
    user: { name: "taro" }
  });
});

app.listen(3000);</code></pre>
<h2>レイアウト共通化: express-ejs-layouts</h2>

<p>毎回 header / footer を include するのが面倒な場合は <strong>express-ejs-layouts</strong> でレイアウトを自動適用できます:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-bash">npm install express-ejs-layouts</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// app.js
const expressLayouts = require("express-ejs-layouts");

app.use(expressLayouts);
app.set("layout", "layout"); // views/layout.ejs を使用
app.set("layout extractScripts", true);
app.set("layout extractStyles", true);</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- views/layout.ejs -->
<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
  <%- style %>
</head>
<body>
  <%- include(&apos;partials/nav&apos;) %>

  <main>
    <%- body %>   <!-- ★ 各ページの中身がここに展開 -->
  </main>

  <%- include(&apos;partials/footer&apos;) %>
  <%- script %>
</body>
</html></code></pre>
<p>各ページからは <code>render('pages/index')</code> するだけで layout.ejs にラップされます。</p>

<h2>他テンプレートエンジンとの比較</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></tr></thead>
<tbody>
<tr><td>EJS</td><td><% %></td><td>include</td><td>素の HTML に近い、JS そのまま使える</td></tr>
<tr><td>Pug (旧 Jade)</td><td>インデント記法</td><td>include / extends / block</td><td>記述量少、独自記法</td></tr>
<tr><td>Handlebars</td><td>{{ }}</td><td>partials / helper</td><td>ロジックレス志向</td></tr>
<tr><td>Mustache</td><td>{{ }}</td><td>partials</td><td>最小限、複数言語対応</td></tr>
<tr><td>Nunjucks</td><td>{% %}</td><td>extends / block / include</td><td>Jinja2 互換、強力な継承</td></tr>
</tbody>
</table>

<h2>パフォーマンス: キャッシュ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 本番環境ではテンプレートをコンパイル結果でキャッシュ
app.set("view cache", true); // NODE_ENV=production で自動 ON

// EJS の cache オプション直接指定
const ejs = require("ejs");
ejs.cache = require("ejs").cache; // LRU</code></pre>
<h2>Next.js / SSR フレームワークとの関係</h2>

<p>近年は Next.js / Nuxt / SvelteKit 等の<strong>コンポーネントベース SSR</strong> が主流で、EJS のような伝統的サーバサイドテンプレートは新規採用が減っています。ただし:</p>

<ul>
<li><strong>管理画面 / 静的多めのサイト</strong>では今でも EJS / Pug が現役</li>
<li>Express で軽量なアプリを作る時に最速</li>
<li>大量のメールテンプレート、PDF 生成テンプレートでも採用例多数</li>
</ul>

<h2>FAQ</h2>

<p><strong>Q: include で <code>Cannot find module</code></strong><br />
A: パスが現在ファイルからの相対です。<code>../</code> が必要なケース多数。<code>app.set('views')</code> も再確認。</p>

<p><strong>Q: 親で定義した変数が子で undefined</strong><br />
A: EJS 3 では基本見えますが、Express の <code>res.locals</code> 経由で渡すか、include の第 2 引数で明示的に渡すのが安全。</p>

<p><strong>Q: ループ内 include は遅い?</strong><br />
A: 大量ループでは EJS 関数呼び出しコストが効きます。本番では view cache を ON にし、必要なら HTML を予めビルドしてください。</p>