Front End Programming

【Gulp】EJSを使ってHTMLを量産する

npmJavaScript

最近GulpでEJSというテンプレートを使ってHTMLのファイルを量産しました。

JavaScriptがそのまま書けるのでとても魅力的でした。類似するPugと比較しつつ、導入の仕方、やincludeを始めとしたメソッドの使い方などをご紹介します。この記事はGulp4バージョン用にコードを修正しました。

このブログの対象者

  • CMSなしでHTMLを量産しなければならない
  • Pugと比べてみたい
  • npm scriptやgulpが多少理解できる

10 ~ 30ページくらいの規模でページのレイアウト構成がある程違うことを想定しています。

EJSとは?

EJS Embedded JavaScript templating.

公式サイト:EJS Embedded JavaScript templating

Embedded JavaScript templating.
埋め込み型JavaScriptテンプレート

EJS6つの特徴

  1. プレーンなJSが使える
  2. 開発に時間がかからない
  3. 構文がシンプル
  4. 早い
  5. デバックがカンタン
  6. 開発がアクティブ

Pugとの比較

テンプレートメリットデメリット
Pugコードが短いルールが独特
インデント間違えたら地獄
EJSコードが長いJSライクに書ける
インデントを気にせず書ける

私はコーダー、デザイナーにはPugがオススメ。コードが短くて済むからです。
コーディングが早く終わる。

まずは基本の「き」から。Gulpを使って導入する

Gulpでの導入方法の紹介です。
まずはテキトーにプロジェクトを格納するフォルダーを作ります。
フォルダーをVS Codeなどのテキストエディターで開き、以下コマンドでpackage.jsonファイル作成。

コマンド
npm init -y

以下コマンドでまとめて、gulp gulp-ejs gulp-rename plumberをインストールします。

コマンド
npm i -D gulp gulp-ejs gulp-rename plumber

以下ディレクトリ構造を参考にしながらファイルやフォルダーを作成してください。

プロジェクトフォルダ/
  ├ package.json(編集)
  │ gulpfile.js(作成・編集)
  ├ src/(作成)
  │  ├ data/(作成)
  │  └ ejs/index.ejs
  └ dist/(自動生成)

package.jsonscriptsに追記。

package.json
"scripts": {
  "start": "gulp"
},

gulpfile.jsのコードを編集します。

gulpfile.js
const { src, dest, series, parallel, watch } = require("gulp");
const ejs = require("gulp-ejs");
const rename = require("gulp-rename");
const plumber = require("gulp-plumber");//エラーでビルドを中止させない

function ejs(done) {
  src(["src/ejs/**/*.ejs"])
    .pipe(plumber())
    .pipe(ejs())
    .pipe(rename({ extname: ".html" }))
    .pipe(dest("dist/"));
  done();
}

exports.default = series(ejs);

EJSで文字列などをHTMLに出力する方法は以下。

<!-- 出力 -->
<%= 【出力内容】; %>

ejsフォルダーにindex.ejs(ejsの拡張子は.ejsです)を作成し、テキトーなhtmlコードを追加します 。

index.ejs
<h1><%= 'Hello, EJS'%></h1>

以下コマンドでコンパイルします。

コマンド
npm start

distフォルダー内にindex.htmlが生成されます。

EJSでdistフォルダー内にindex.html生成

headerとfooterなどのファイルを分け、テンプレート化する

それではテンプレート化してみましょう!
ejs/_inc/ディレクトリーに、_header.ejs_footer.ejsを作ります。

プロジェクトフォルダ/
  ├ package.json
  │ gulpfile.js(修正)
  ├ src/
  │  ├ data/
  │  └ ejs/index.ejs
  │    └ _inc/
  │      ├ _header.ejs(作成)
  │      └ _header.ejs(作成)
  └ dist/ index.html(自動生成)

Gulpのタスクを修正。接頭辞に-(ハイフン)がつくejsファイルはコンパイルから外します。

gulpfile.js
function ejs(done) {
  src(["src/ejs/**/*.ejs", "!" + "src/ejs/**/_*.ejs"])//修正
    .pipe(plumber())
    .pipe(ejs())
    .pipe(rename({ extname: ".html" }))
    .pipe(dest("dist/"));
  done();
}
<!-- 変数 -->
<% let 【変数名】 = 【変数の中身】; %>
<!-- ファイルのインクルード -->
<%- include(【相対パス+ファイル名】, { 【変数など値をインクロード先に渡す】 }) %>

index.ejsの中身を書き換えます。

index.js
<% let title = 'トップページ'; %>
<% let description = 'ディスクリプション'; %>
<% let pageId = 'home'; %>
<%- include('./_inc/_header', { title: title, description: description, pageId: pageId }) %>

<h1><%= 'Hello, EJS'%></h1>

<%- include('./_inc/_footer')  %>

_header.jsでif文を使って、トップページの時はサイト名をh1タグ、それ以外はトップに戻れるように調整します。

_header.js
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="format-detection" content="telephone=no">
  <title><%= title %></title>
  <meta name="description" content="<%= description %>">
  <link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
  <header>
    <% if (pageId === 'home'){ %>
      <h1>サイトタイトル</h1>
    <% } else { %>
      <p><a href="/">サイトタイトル</a></p>
    <% } %>
  </header>
  <main>

_footer.ejsにもコンテンツを追加します。

_footer.js
  </main>
  <footer>
  フッター
  </footer>
</body>
</html>

ライブリロードとコードをキレイにするnodeモジュールを追加

BrawserSyncgulp-html-beaitufyでもっと作業しやすくします。

コードが長くなってしまいましたのでgulpfile.jsのコードごっそり載せます!

gulpfile.js
const { src, dest, series, parallel, watch } = require("gulp");

//ライブリロード
const bs = require("browser-sync").create();

const ejs = require("gulp-ejs");
const rename = require("gulp-rename");
const plumber = require("gulp-plumber");
const htmlbeautify = require("gulp-html-beautify");

// browserSync初期化
function bsInit(done) {
  bs.init({
    server: {
      baseDir: "./dist",
    },
    reloadDelay: 1000,//リロードの遅延
    open: false, //起動時のライブリロードを止める
  });
  done();
}

function ejs(done) {
  src(["src/ejs/**/*.ejs", "!" + "src/ejs/**/_*.ejs"])
    .pipe(plumber())
    .pipe(ejs())
    .pipe(
      htmlbeautify({
        indent_size: 2, //インデントサイズ
        indent_char: " ", // インデントに使う文字列はスペース1こ
        max_preserve_newlines: 0, // 許容する連続改行数
        preserve_newlines: false, //コンパイル前のコードの改行
        indent_inner_html: false, //head,bodyをインデント
        extra_liners: [], // 終了タグの前に改行を入れるタグ。配列で指定。head,body,htmlにはデフォで改行を入れたくない場合は[]。
      })
    )
    .pipe(rename({ extname: ".html" }))
    .pipe(bs.stream()) // 変更を感知
    .pipe(dest("dist/"));
  done();
}

// ファイルの監視
function watchTask(done) {
  watch(["src/ejs/**/*.ejs"], ejs);
  done();
}

exports.default = parallel(series(bsInit, ejs, watchTask));

以下のコマンドで起動します。

コマンド
npm start
こちらが出力されたコードです。うん、美しい❤️ 出力されたコードも美しい

いよいよページ量産。jsonファイルを使ってコンテンツを管理する

EJSのいいところは、ファイル構造を保ったままコンパイルできます。

データ管理するためにJSONデータを利用します。

JSONデータをEJSで利用するために、node moduleのfsを追加します。

コマンド
npm i fs -D

gulpfile.jsにコードを追加してください。

gulpfile.js
const fs = require("fs");//追加

//省略

function ejs(done) {
  const json_path = "./src/data/site.json";  const json = JSON.parse(fs.readFileSync(json_path));
  src(["src/ejs/**/*.ejs", "!" + "src/ejs/**/_*.ejs"])
    .pipe(plumber())
    .pipe(
      ejs({        jsonData: json,      })    )
    //省略
    .pipe(bs.stream());
  done();
});
//省略
プロジェクトフォルダ/
  ├ package.json
  │ gulpfile.js
  ├ src/
  │  ├ data/
  │  │  └ site.json(追加)
  │  └ ejs/
  │    ├ index.ejs
  │    ├ contact/index.ejs(追加)
  │    ├ about/index.ejs(追加)
  │    ├ work/index.ejs(追加)
  │    └ _inc/
  │      ├ _header.ejs
  │      └ _header.ejs
  └ dist/
     ├ index.html
     ├ contact/index.html
     ├ about/index.html
     └ work/index.html

site.jsonファイルを作成しdada/内に追加。基本のサイト情報やページごとの設定を書いていきます。

site.json
{
  "siteInfo": {
    "name": "サイト名",
    "siteUrl": "サイトのドメイン"
  },
  "pages": {
    "home": {
      "name": "ホーム",
      "description": "ページの説明"
    },
    //・・・・ページ分追記作成
  }
}

ちゃんとデータが取得できるか確認します。
index.ejsの空きスペースに以下コードを仕込んでみてください。

index.ejs
<% console.log(jsonData) %>

index.ejsを修正します。
基本情報はJSONデータで管理するのでpageIdだけあればOKです。

index.ejs
<% let pageId = 'home'; %>
<%- include('./_inc/_header', { pageId: pageId }) %>

ここにコンテンツ
<%- include('./_inc/_footer', {pageId: pageId })  %>

_header.ejsを修正します。
ページタイトルはトップページの時のみサイト名を出力するようにしています。
ページのメニューをforin文でループして書き出します。pageIdと項目名が一致する時は、リンクを外し、クラスcurrentを付与しています。

_header.ejs
<% let title = (pageId === 'home') ? jsonData.siteInfo.name : jsonData.pages[pageId].name + jsonData.siteInfo.name%>
<% let description = jsonData.pages[pageId].description %>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="format-detection" content="telephone=no">
  <title><%= title %></title>
  <meta name="description" content="<%= description %>">
  <meta name="description" content="">
  <link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
  <header>
    <% if (pageId === 'home'){ %>
      <h1><%= jsonData.siteInfo.name %></h1>
    <% } else { %>
      <a href="/"><%= jsonData.siteInfo.name %></a>
    <% } %>
  </header>
  <nav>
    <ul>
      <% for(let page in jsonData.pages){ %>
        <% if (page === pageId) { %>
          <li class="current"><%= jsonData.pages[page].name %></li>
        <% } else { %>
          <li><a href="/<%= page %>/"><%= jsonData.pages[page].name %></a></li>
        <% } %>
      <% }; %>
    </ul>
  </nav>
  <main>

_footer.ejsも似た感じで修正します。

_footer.ejs
  </main>
  <footer>
    <nav>
      <ul>
        <% for(let page in jsonData.pages){ %>
          <li><a href="/<%= page %>/"><%= jsonData.pages[page].name %></a></li>
        <% }; %>
      </ul>
    </nav>
    <p><small>(c)<%= jsonData.siteInfo.name %></small></p>
  </footer>
</body>
</html>

work、about、contactなどのファイルを作成します。
階層が変わるのでインクルードファイルのパスを間違わないように注意してください。

work.ejs
<% let pageId = 'work'; %>
<%- include('./../_inc/_header', {pageId: pageId }) %>

<h1><%= jsonData.siteInfo.name%></h1>
<%- include('./../_inc/_footer', {pageId: pageId })  %>
出力されたコードも美しい

おまけ。EJSお役立ち情報

EJS使い方

JSと使い方は一緒なので、特殊な書き方などだけ抜粋しておきます。

コード説明
<% %>この中にjsを書ける
<%= %>出力。エスケープする
<%- %>出力。エスケープしない
<% let text = "文字" %>変数。文字などを代入
<%# コメント %>htmlに出力されない。ここの中で変数宣言とかしても無効
<% include _inc/_header %>ファイルのインクルード
<%- include('_inc/_header', {data: 'test'}) %>ファイルのインクルード引数を渡すバージョン

VS Codeの設定

VS Codeの設定を変えて、さらに快適にコードを書きましょう!

Emmetを使えるようにする

EJSでもエメットを使えるようにしておきましょう。
設定から設定JSONを開き、“emmet.includeLanguages”に以下を追加します。

  "emmet.includeLanguages": {
    "ejs": "html"//これを追加。
  },
  // 省略

拡張機能.ejsを入れてシンタックスハイライターを使う

慣れないと何で詰まってるかわからなくなります。
.ejsという拡張機能をVS Codeに入れておきましょう。

拡張機能.ejsを入れてシンタックスハイライターを使う

インストールしたら再起動しましょう。

まとめ・EJSはJSライクに書け、量産に向いている!

今回は10 ~ 20ページ程度で量産する方法をまとめました。
パンくずリストやコンバージョンエリアなどのインクルードファイルをさらに細かく分けたら、さらに保守性の高いサイトが作れそうですね!

今の時代でも、セキュリティ上どうしてもHTMLの静的ファイルで作らないといけないこともあります。

ページ数が多いと大変だけど、導入の敷居が、、、なんてこともありますが、EJSならさほど学習コストもかからない気がします!

この記事が皆さんのコーディングライフの一助となれば幸いです。

最後までお読みいただきありがとうございました。

EJSの関数に関しても記事を書きました

関数についてはこちらを参考にしてください。
EJSの関数を使って、pictureタグの記述を効率化

最近のウェブ制作はスマホ、タブレット、多様化したブラウザやディスプレイへの画像対応が大変です。画像やディスプレイの出し分...

お読みいただきありがとうございます。
「銀ねこアトリエ」をより良いブログにするために是非応援してください!

銀ねこアトリエを応援する