実践編 – EJSでサイト制作効率化

実践編 – EJSでサイト制作効率化

この記事は「EJSでサイト制作効率化」の第2回目の記事です。

過去回は以下になりますので、まだ読まれていない方はぜひこちらもよろしくお願いします。

はじめに

EJSは、Embedded JavaScriptの名の通り、HTML内にJavaScriptを埋め込む作りになっています。
そのため、JavaScriptに慣れている方は抵抗なく使うことができるでしょう。

今回は、デザイナーやコーダーなど、JavaScriptに慣れてない方がなるべく抵抗を感じないように一つずつ解説を行います。

さて、EJSはJavaScriptに由来する様々な構文が搭載されています。
今回は、その中でもよく使う構文を取り上げて解説していきます。

表記ゆれは「変数」で解決

HTML内にサイト名やサービス名、事業者名を記述することは多々あると思います。
特にサイト名は、<title>要素、<meta>要素、<h1>要素、その他色々な部分で用いることがあります。

その際に、「表記ゆれ」を厳密に考える必要がある場合があります。
例えば、当メディアであれば、思いついたものだけでも下記のようにゆれが発生します。

大文字小文字

  • to-R Media
  • to-R media

読み方の表記はつけるのか?また、正式な表記は?

  • to-R Media(トゥーアールメディア)
  • to-R Media(トゥーアール・メディア)

全角半角

  • to-R Media
  • to-R Media

このようになっていた時、表記ゆれをいちいち修正して回るのは面倒ですし、検索や目視では限界があります。

また、これらは、誰でもできる「ただの作業」 であり、デザイナーやエンジニアの工数をそこに割くのは有意義ではありません。

こういう時こそ、機械の正確な仕事に頼るべきです。
このようなケースでは、「変数」を用いることで効果的な解決ができます。

変数の使い方

変数とは、 あらかじめデータに固有の名前を付けておき、必要な時に呼び出して使えるようにするもの です。

変数を定義する

呼び出すための変数は、EJSファイル内にて、以下のように記述することで定義されます。

<% var value = '定義する文字列'; %>

(JavaScriptですので、;を忘れないようにしましょう。)

この変数は、記述されたEJSファイル内と、それを読み込んだEJSファイル内で有効です。
例えば、header.ejsのように外部化された共通パーツに記述した変数などはすべてのページで有効になります。

また、EJSファイルに直接記述する他に、JSONファイルなどで外部にまとめておくことで、gulpでコンパイルする時に紐付けて流し込むようにすることができます。
(JSONファイルついては別章で解説します。)

変数を呼び出す

EJSにて、変数を呼び出す時は下記の2つのどちらかの様に記述します

<%- site-name %>
<%= site-name %>

このように <%- %><%= %>で囲むことで、あらかじめ宣言した変数が呼び出されます。
これらの違いは、後者はエスケープが適用されるという点にあります。

※エスケープとは

エスケープとは、`¥`や`>`などの特殊文字を記号文字列に置き換える処理を言います。
HTMLタグ込みの`<h1>サイト名</h1>`などを変数として定義していた場合に処理が変わります。

例えば、変数を定義したあとに呼び出す場合、次のように記述します。

<% var siteName = '<h1>サイト名</h1>'; %>

<%- siteName %>

<%= siteName %>

コンパイル後は次のようになります。
<%= %>で囲んだ方は、特殊文字が実体参照テキストに置き換えて出力されていることに注目です。

<h1>サイト名</h1>
&lt;h1&gt;サイト名&lt;/h1&gt;

特殊文字をタグとして使いたい場合は、<%- %>を用いましょう。
また、ただ文字列として特殊文字を用いたい場合は<%= %>を用いましょう。

以上のようにすることで、変数の表記が正しければ、変数を呼び出した部分の表記はすべて正しい という前提ができます。
これにより、内容チェックにかかる労力も減らすことができます。

これが、EJSのメリットの 一定の品質を保てる ということです。

変数の使い方は色々

EJSにおいて変数を使う目的は、次のような点があげられます。

1.サイト共通の単語を使い回すため

サイト共通の単語を、間違いなく、楽に表記するという目的で変数を使うということができます。

2.JSONファイルで管理するため

テンプレートを徹底的に活用するならこちらです。

例えば、クライアントのレギュレーションがあって、毎回同じHTMLテンプレートを用いて中身のみ編集する際などに力を発揮します。

あらかじめそれに対応するJSONファイルを作成しておけば、新規立ち上げの際のスピードが圧倒的に違います。

しかも、JSONファイルはただの対応表のようなものなので、空箱だけ用意しておけば事前知識がなくても作ることができます。

例えば、仕様を管理している人が作れば、色々な手間が省けていいのではないでしょうか。

JSONファイルとは?

JavaScript Object Notationの頭文字を取ったものです。
データをやりとりするときに用いられます。
人間が読み書きしやすく、機械も解析しやすい形式で、JavaScriptに限らず多くのプログラミング言語で用いられています。

JSONファイルとの組み合わせ

EJSとJSONファイルと組み合わせることで、より効率的なサイト制作ができます。

ここでは、それらをどのようにして紐付けていくのかを順番に解説します。

1.JSONファイルを読み込み、解析する。

gulpfile.js内にて下記のように記述することで、JSONファイルを読み込むことができます。

var json = JSON.parse(fs.readFileSync(JSONファイルのパス));

まず、JSON.parse()というJavaScriptのメソッドを用います。
これは、「文字列を JSON として解析する」 というメソッドです。

JSON.parse() – JavaScript | MDN

「文字列を解析する」、メソッドなので、()の中には文字列を渡してあげる必要があります。
SONファイルのパスを直接引き渡しても、JSON.parse()メソッドは読み込んで処理してくれません。
それはファイルの在り処にすぎないからです。

そこで、用いるのがfs.readFileSync()です。
これは、Node.jsの標準搭載機能fsのメソッドです。
引き渡したファイルパスの 内容 を返します。

そのためfs.readFileSync()そのものは文字列として扱え、JSON.parse()で解析できるようになります。

fsとは、Node.jsの基本機能「File system」のことです。

Node.js v7.3.0 Documentation

Node.jsの基本機能ですので、npmが使用できる状態ならば、gulpfile.jsで読み込む記述をすることで使えます。

const fs = require('fs');

2.EJSにデータを引き渡す

1で、JSONファイルとして解析したデータが手に入りました(後で使えるように、JSONという名前をつけて変数化しています)。
続いて、EJSに引き渡していきます。

gulpfile.jsのタスクは下記の通りです。

gulp.task('ejs', function() {
    const json_path = "./JSON/test.json";//JSONファイルの場所
    const ejs_path = "./src/ejs/index.ejs";//ejsファイルの場所

    const json = JSON.parse(fs.readFileSync(json_path)); // JSONの読み込み

    gulp.src(ejs_path)
      .pipe(ejs({
        jsonData: json // JSONのデータをejsに渡す
      },
      {ext: '.html'}//htmlファイルとして出力
      ))
      .pipe(gulp.dest("./dist"));
});

(gulp-ejsの仕様として、{ext: '.html'}の記述がないと.ejsファイルとして出力されてしまうので、注意が必要です。)

これで、EJSファイルとJSONファイルの紐付けができました。

3.EJSファイル内で変数を呼び出す

EJSファイルとJSONファイルの紐付けが完了したので、EJSファイル内でJSONファイルに定義した変数を呼びだす記述ができるようになりました。

では、実際にEJSファイルに呼び出すための記述を書いていきましょう。

例として、JSONファイルを次のように作成しました。

{
  "name": "to-R,inc",
  "description":"株式会社トゥーアールは、渋谷のフロントエンド専門の制作会社です。"
}

EJSファイルは下記のように記述しています。
<%= %>で囲った部分に変数を呼び出す宣言がされていることに注目です。

<header>
<h1><%= jsonData.name %></h1>
<%= jsonData.description %>
</header>

<footer>
<small>© 2016 <%= jsonData.name %> All Rights Reserved.</small>
</footer>

コンパイル結果は次の通りです。
それぞれ変数を呼び出している部分に、JSONに対応した値が入っていることがわかります。

<header>
<h1>to-R,inc</h1>
株式会社トゥーアールは、渋谷のフロントエンド専門の制作会社です。
</header>

<footer>
<small>© 2016 to-R,inc All Rights Reserved.</small>
</footer>

これで、JSONファイルに変数を定義し、EJSファイルで呼び出してコンパイルすることができました。

JSONファイルの取扱注意点

JSONファイルの形式は複数あります。

1つは下記のような形

{
  "name1":"data1",
  "name2":"data2".
  "name3":"data3"
}

もう1つは下記のような形です。
複数の配列が存在する状態です。
{},で区切ったものを、[]で括っています。

[
 {
   "name1":"data0-1",
   "name2":"data0-2",
   "name3":"data0-3"
 },
 {
   "name1":"data1-1",
   "name2":"data1-2",
   "name3":"data1-3"
 }
]

もしくは、下記のように、配列に名前が付いている場合です。
配列に名前がつく場合、それらをさらに{}でくくる必要があるので注意しましょう。

{
    "array0": [
      {
        "name1":"data0-0-1",
        "name2":"data0-0-2",
        "name3":"data0-0-3"
      },
      {
        "name1":"data0-1-1",
        "name2":"data0-1-2",
        "name3":"data0-1-3"
      }
    ],
    "array1": [
      {
        "name1":"data1-0-1",
        "name2":"data1-0-2",
        "name3":"data1-0-3"
      },
      {
        "name1":"data1-1-1",
        "name2":"data1-1-2",
        "name3":"data1-1-3"
      }
    ]
}

今回の場合が少し癖があり、データをEJSに渡すときに 「何番目の配列を渡すのか」 ということを宣言しなければなりません。
例えば、下記のように宣言する必要があります。

 jsonData: json[0] // JSONのデータの0番目の配列をejsに渡す

配列は、0番目、1番目という風に「0」からカウントします。
上記のように宣言することで、0番目、すなわち

{
  "name1":"data0-1",
  "name2":"data0-2",
  "name3":"data0-3"
}

がejsに引き渡されます。

配列に名前が付いている場合も、考え方は同じです。
JSONデータのうち、array1配列の中の、0番目の配列、というイメージです。

  jsonData: json.array1[0] // JSONのデータをejsに渡す

このように、多次元配列では「何番目の配列」及び「なんという名前の配列の、何番目の配列」を引き渡すかの宣言が必要です。

includeの使い方

第一回でも取り上げたincludeタグをおさらいしましょう。
以下のようなディレクトリ構造のときを考えます。

/
|--_partial
|  |--header.ejs
|  |--footer.ejs
|--index.ejs

index.ejs上で、外部ファイル化したheader.ejs及びfooter.ejsを読み込みたいとき、記述はこのようになります。

<% include _partial/header %>
<main>メイン部分テキスト</main>
<% include _partial/footer %>

このように<% include [相対パス] %>と記述することで、対応するファイルを読み込みます。

include時に引数を渡す

includeを用いるとき、引数を渡すことができます。
記述は以下のように行います。

<%- include('./_partial', {datalabel:'data'}) %>

(includeとカッコの間にスペースが開かないことと、囲む記号が<%- %>であることに注意してください。)

これにより、共通パーツでも渡す引数によって変化をつけることができるのです。

引数を渡して状態を変化させる

簡単な例として、「ある状態のページのみ、ヘッダーに出現するモジュールがある」場合を考えます。

この場合、ただ単純に「見た目違いのheader2.ejs」のようなファイルを作って読み込ませるという手段もありますが、もしヘッダーに修正が入った場合に修正するファイルが二つになり、手間がかかります。

そこで、下記のようにifと組み合わせることでf変化をつけることができます。

ここでは「ログイン時はマイページへのリンクが出現する」という状態を想定します。

if文の分岐条件がstatus==loginなので、それを満たすように{status:"login"}というパラメータを引き渡します。

次のように記述します。

<%- include('_partial/header', {status:'login'}) %>
<main>メイン部分テキスト</main>
<% include _partial/footer %>

読み込まれるheader.ejsは次の通りです。

<header>
ヘッダー
<% var status; if(status == 'login') { %>
<a href="mypage.html">マイページへ</a>
<% }%>
</header>

コンパイル結果は次の通りです。

<header>
ヘッダー 
<a href="mypage.html">マイページへ</a>
</header>
<main>メイン部分テキスト</main>
<footer>フッター</footer>

また、通常通りの、引数を渡さないincludeを行った場合は、if内のstatus == 'login'が満たされないため、その中の記述は無効になります。

これで、引数を渡すことによる状態の分岐ができるようになりました。

for文で仮モジュールをループ

特にメディアサイトなどで多いのが、トップページに記事カードを複数個並べる、などということ。

WordPressなどで動的に組み込む場合は、Wordpressのループが用いられますね。

同一モジュールの配置は、単純にコピペすれば済む話ではありますが、たかが仮モジュールのために何百行も記述すると、見通しが悪くなります。
また、修正が入ったらコピペのやり直しです。

ここで登場するのがfor文です。

<% for ( var i = 0; i < 4; i++ ) { %>
<div class="media-card"><img class="media-card__thumbnail" alt="">
<h2 class="media-card__title">仮タイトル</h2>
ダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキストダミーテキスト</div>
<% } %>

このように<% for ( var i = 0; i < 4; i++ ) { %><% } %>で囲むことで、任意の回数ループします。

例えば、上記の例では4回ループするため、for内の.media-cardモジュールは4つ記述されます。

数値を変えることにより、「〇〇個並んだときの見た目を確認したい」という時も簡単に要素が増減できます。
数を数えながらコピペするよりも断然正確で、コードも煩雑になりません。

if文で状態管理する

例えばよくあるのが「ナビゲーションの現在地はスタイルを変えて」という要件。

SMACSSやFLOCSSを取り入れている場合は、.is-currentクラスの付け外しで見た目を変えていくことが多いかと思います。

その場合、通常であれば、各ページのナビゲーションにてクラスを付け替える作業が発生します。

しかし、EJSを使えば、共通のナビゲーションモジュールとして外部化し、if文を使ってモジュール内部で分岐を行うことで、「1ファイルだけ編集すれば、読み込むすべてのファイルで編集が適用され、場合分けもできる」という非常に効率的な状態になります。

サンプル

読み込まれるナビゲーションのEJSファイルは下記の通りです。
各々の<li>のクラスがlocationの値によって分岐するような形になっています。

<ul class="global-nav">
<li class="global-nav__item <% if(location == 'top'){ %>is-current<% } %>"><a href="#">TOP</a></li>
<li class="global-nav__item <% if(location == 'about'){ %>is-current<% } %>"><a href="#">このサイトについて</a></li>
<li class="global-nav__item <% if(location == 'profile'){ %>is-current<% } %>"><a href="#">著者プロフィール</a></li>
<li class="global-nav__item <% if(location == 'contact'){ %>is-current<% } %>"><a href="#">お問い合わせ</a></li>
</ul>

読み込む側では下記のように記述します。
ポイントは{location:top}というパラメータを引き渡している点です。

<header>ヘッダー</header>
<nav>
<%- include('_partial/nav.ejs', {location:'top'}) %>
</nav>
<footer>フッター</footer>

コンパイル結果は次のようになります。
location == topという条件に合致したため、1つ目の<li>.is-currentが付与されていますね。

  
<header>ヘッダー</header>
<nav>
<ul class="global-nav">
<li class="global-nav__item is-current"><a href="#">TOP</a></li>
<li class="global-nav__item "><a href="#">このサイトについて</a></li>
<li class="global-nav__item "><a href="#">著者プロフィール</a></li>
<li class="global-nav__item "><a href="#">お問い合わせ</a></li>
</ul>
</nav>
<footer>フッター</footer>

また、ここでlocationincludeの際に引数として渡しましたが、ページそのものに<% var location == "top" %>のように指定しておくという方法もあります。この場合、引数を渡さずにincludeしても処理が行われます。

これらは、局所的に引数を渡すべきなのか、ページ固有で持つべき変数なのか、などで適宜使い分けていきましょう。

まとめ

  • 変数を定義するときは<% var value = '定義する文字列'; %>
  • 呼び出すときは<%= value %>または<%- value %>
  • includeは引数とifを組み合わせると状態管理できる
  • for文は同じ記述の繰り返しをすっきり見せられる

ここまでに解説した技術は以下の通りです。

  • 変数
  • include
  • for
  • if
  • JSON

たった5つのテクニックだけでも、かなりサイト制作は効率化できます。

また、重要なのは、EJSはあくまでツールということです。

EJSを使いこなすことが大事なのではなく、EJSを使って浮かせた時間をどうやって使うか考えることが重要だということを忘れないようにして、ツールとうまく付き合っていきましょう。