Front End Programming

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

JavaScriptnpm

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

JavaScriptがそのまま書けるのでとても魅力的でした。もともと使っていたPugと比較しつつ、導入の仕方をご紹介いたします。

この記事を書いた人

かみーゆ/フロントエンドエンジニア

セブ島在住の気ままな海外ノマドエンジニア。IT業界10年。テクニカルディレクター・エンジニア講師・ブリッジSEを経て今に至る。CMS concrete5エバンジェリスト。テックブログ以外も「磨耗しない人生の選択」や「海外生活」のライフスタイルについて発信。好きなものは肉とビール。

Read More

このブログの対象者

  • 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がオススメ。コードが短くて済むからです。
コーディングが早く終わる。

pug(旧:Jade)を使い倒して時短マークアップしよう!
pug(旧:Jade)を使い倒して時短マークアップしよう!

私はページを量産したりチームでサイトを作る時Pugという言語を使ってWebサイトを作って・・

まずは基本の「き」から。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/(自動生成)

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

const gulp = require("gulp");
const ejs = require("gulp-ejs");
const rename = require("gulp-rename");
const plumber = require("gulp-plumber");//エラーでビルドを中止させない

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

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

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

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

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

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

gulp ejs

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ファイルはコンパイルから外します。

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

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

<% 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タグ、それ以外はトップに戻れるように調整します。

<!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にもコンテンツを追加します。

  </main>
  <footer>
  フッター
  </footer>
</body>
</html>

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

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

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

const gulp = 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");

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

// ファイルの監視
gulp.task("watch", function () {
  gulp.watch(["src/ejs/**/*.ejs"], gulp.task("ejs")); //追加
});

gulp.task("ejs", function (done) {
  return gulp
    .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(gulp.dest("dist/"))
    .pipe(bs.stream());//追加
  done();
});

gulp.task(
  "default",
  gulp.series(gulp.parallel("ejs"), gulp.parallel("watch", "bs-init"))
);

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

gulp

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

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

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

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

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

npm i fs -D

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

const fs = require("fs");//追加

//省略

gulp.task("ejs", function (done) {
  //ここから追加
  const json_path = "./src/data/site.json";
  const json = JSON.parse(fs.readFileSync(json_path));
  //ここまで追加

  return gulp
    .src(["src/ejs/**/*.ejs", "!" + "src/ejs/**/_*.ejs"])
    .pipe(
      //ここから追加
      ejs({
        jsonData: json,
      })
    )
    //ここまで追加
    //省略
    .pipe(gulp.dest("dist/"))
    .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/内に追加。基本のサイト情報やページごとの設定を書いていきます。

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

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

<% console.log(jsonData) %>

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

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

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

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

<% 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も似た感じで修正します。

  </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などのファイルを作成します。
階層が変わるのでインクルードファイルのパスを間違わないように注意してください。

<% 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ならさほど学習コストもかからない気がします!

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

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

「銀ねこアトリエ」のブログを定期購読しよう

Feedlyに登録する