EJSとJSONから大量のHTMLを生成するためのメモ
フルスクラッチでゴリゴリHTMLを書いていると避けて通れないのがHTMLテンプレートエンジンの問題。少し前にこんなツイートをしていて、最近はもっぱら html-include をメインに使っていたのだが、ファイル数が数十を超えてくるとやはりEJSが必要になる。
大げさなフレームワーク使うほどじゃないけどカジュアルにHTMLインクルードしたいときの方法の変遷
— 高橋廸久(道久) (@neriu) 2020年6月3日
けっこう前→SSI
かなり前→PHPのインクルード
ちょっと前→EJS
少し前→gulpでがちゃがちゃやる
最近→html-includes
PHPとEJSはなんでもできるっちゃできるので今でも現役#npm #ejs
しかし利用頻度が低いと使うたびにググり直しになるのが辛いので、ここにEJSの始め方をメモしておくことにする。
EJSの本家サイト
ejs.co
想定しているシーン
たくさんのHTMLファイルがあって、タイトルやDescriptionやCanonicalやページごとのclassなどのページのメタ情報をHTMLに直書きするのは辛い・・・みたいなシーンを想定している。要約するとこうだ。
メタ情報をサイト全体で体系的かつ一元的に管理したいと考え「そうだ、まずエクセルにまとめよう」ってなるのはサイト制作あるあるだが、いざそれを元にサイト作って、その後修正やページ追加なんかのタイミングで時間もないしとりあえず簡単だしお腹すいたからっていう理由でHTMLだけを直しちゃってエクセルとズレが生じた瞬間もうエクセルは役立たずになりあとはもうずっとHTMLを個別に修正していくしかない、そのうちぐちゃぐちゃになって「ちょっと男子〜!メタ情報はもっとこうサイト全体で体系的かつ一元的に管理しなきゃダメでしょ〜!」みたいになって「よし!それじゃあ今のHTML全部総ざらいして、メタ情報をまずはエクセルにまとめよっか!」ってなって・・・(以下ループ)・・・みたいなあるあるなシーンを阻止したいというシーン。
を想定している(早口)。
こんなイメージ
0. 準備
npmがインストールされていればそれでOK。
1. EJSに必要なnpmパッケージのインストール
gulpに頼らなくてもできるのだと思うが、とりあえず慣れてるので...。gulp、gulp-ejs、gulp-rename、fsの4つのパッケージをインストールする。
$ npm i -D gulp gulp-ejs gulp-rename fs
fsはファイルの入出力をするパッケージで、今回はJSONの読み込みに利用する。gulp-ejsがEJS本体で、gulp-renameはEJSで出力されたファイル拡張子を .ejs から .html に変換するのに使う。
2. メタ情報を管理するExcelファイルを用意する
ページごとにタイトルとdescriptionを管理するというごくシンプルな例だと、ExcelのA列にページID(p001でもtopでもページを一意に識別できればなんでも良し)。B列にタイトル、C列にdescriptionがあり、それがページ数分だけの行数があるイメージ。便宜上Excelと書いたがわかりやすければなんでもいい。
3. ExcelをJSONに変換する
ここはいろいろツールがあるのだろうが、私の場合はここを使っている。
このページ左側のテキストボックスに、Excelで全選択コピーしたものを貼り付ける。次にOutputに「Hash」を選択する。最後に「Convert」をクリック。以上。
ちなみに「Hash」がキモで、こうするとエクセルのA列すなわちページIDをキーとしたJSONデータが出来上がる。するとEJSで値を参照するときに「data['p001'].title」みたいにできて便利。
これで以下のようなJSONが出力されるのでファイルに貼り付けて保存する。
例: page-data.json
{ "p000": { "title": "ホーム | サイト名", "description": "すごくイケてる会社のホムペです。" }, "p010": { "title": "製品情報 | サイト名", "description": "イケてる製品情報を紹介します。" }, (以下略) }
4. gulpfile.js でEJSのgulpタスクを定義する
下記の例では、ejsフォルダの*.ejsファイルをEJSが処理してdistフォルダに出力する。EJSにはJSONデータ(例ではpage-data.json)を渡している。後述するが、EJSファイルでは渡されたJSON(ページごとのメタデータ)を適宜差し込んだ上でHTMLを組み立てる。そんな処理になっている。
例: gulpfile.js
var gulp = require('gulp'); var fs = require( 'fs' ); var ejs = require('gulp-ejs'); var rename = require('gulp-rename'); gulp.task('ejs', function(done) { var json = JSON.parse(fs.readFileSync('./ejs/page-data.json', 'utf-8')); gulp.src([ './ejs/**/*.ejs', '!' + './ejs/**/_*.ejs' ]) // 第1引数がEJSの処理対象とするファイル、第2引数が除外ファイル .pipe(ejs({ pageData: json })) // これがEJS本体処理。jsonデータを渡してる .pipe(rename({ extname: '.html' })) // 拡張子をhtmlにする .pipe(gulp.dest('../dest/')); // 出力先フォルダ done(); });
5. EJSファイルを用意する
EJSファイル内で使える変数や構文などは解説しているサイトがたくさんあるので割愛する。ここではJSONデータをどうやって展開するかだけ説明する。といっても実に簡単。まずは某ページ(仮にページID「p001」とする)のEJSファイル本体。%で囲まれたEJSの世界以外は普通にHTMLで記述する。
例: index.ejs(なんか適当なページの本体ファイル)
<% const meta = pageData['p001']; %> <%- include('inc/_header.ejs', { 'meta': meta }); %> <div class="main"> (略。ここは普通にHTMLで記述) </div> <%- include('inc/_footer.ejs'); %>
pageDataにはgulpfile.jsのタスクで渡されたJSONデータがまるっと入っているので、その中のページID「p001」の情報のみを取り出して変数 meta に入れている。そしてこの変数 meta を共通ヘッダファイル(inc/_header.ejs)に渡している。ついでに共通フッタ(inc/_footer.ejs)も読み込んでいるが、こちらは全ページ同じなのでデータ受け渡しはなし。
ひとつ注意はEJSタグの使い方。「<% ...%>」で囲まれた範囲は基本はJavaScript構文の世界。それに対して「<%- ... %>」のようにハイフンがつくと、変数やインクルードファイルをHTMLとして展開してそのまま出力する世界となる。「<%-変数名%>」のようにすると、その変数の中身をシンプルに出力できてGood。逆にハイフンなしの「<% ...%>」にinclude構文を書いても何も出力されないので注意。コンパイルエラーにもならないのでハマりがち。
あとファイル名先頭にアンダースコア(_)が付加されているejsファイルは、gulpfile.jsにて出力対象外としている。アンスコつけ忘れると、destフォルダに「inc/header.html」などという意味のないファイルができてしまう。そういう仕組み。
続いて共通ヘッダファイルそのもの。今回のゴールであるページのメタ情報を出力する本体のファイルとなる。
例: inc/_header.ejs(全ページで使う共通ヘッダ)
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title><%- meta.title %></title> <meta name="description" content="<%- meta.description %>"> (以下略)
変数 meta で渡された情報を「<%-変数名%>」で出力している。今回はページタイトルとDescriptionだけだが、同じ要領でOGPタグやページ別のclass名やその他なんでもできてしまう。
6. gulpのejsタスクの実行
いよいよHTMLの生成。gulpfile.jsでタスク名を「ejs」としているのでgulpfile.jsのあるディレクトリでこうする。
$ gulp ejs
数十〜数百くらいのEJSファイルなら一瞬でdestフォルダにhtml出力してくれる。ejs配下のフォルダ構成も維持してくれる。gulpのタスクでejsフォルダをwatchしておけば、ファイル編集する都度htmlを書き出してくれる。
まとめ
必要なのは次のとおり。
- npmパッケージ(gulp, gulp-ejs, gulp-rename, fs)のインストール
- エクセルファイル
- JSON
- gulpfile.js
- EJSファイル
今回は使っていないがEJSは普通にJavaScriptの構文であり、条件分岐もループも使える。なのでたとえば定型化された製品情報をエクセルで持たせておいてEJSで出力といった使い方もできる。エクセルやGoogleスプレッドシートなら誰でも(クライアントでも)編集できるし、データベースやその管理システムなどの大げさな仕組みが不要というのがEJSを使う利点だ。
またEJSに限らずだが、複数人での作業も上記と package.json をgitで共有して $ npm install してもらえばそこからすぐに開発に参加してもらえる。