Nunjucks + gulp で静的 HTML をモジュール化する

やりたいこと

概要

静的 HTML を複数ページ作成するときに、共通部分(ヘッダーとかフッターとか)をコピペしたくない。

「PHP とか使って include とかすればいいじゃん」

ていう意見もあるんだけど、動作確認に web サーバー立てるの面倒だし、単なる HTML で済むなら HTMLで 完結させたい。

細かい背景

gulp-ect を使って、やりたいことは大体実現出来ていたんだけど、以下の2つの理由により、別の方法に切り替えたいと思った。

  • ECTgulp-ect も最終更新日が3年前
  • ファイルのパスを埋め込む(後述)、というののやり方が分からなかったし出来なさそう

やったこと(TL;DR)

さきに結論だけ書いておくと、

  • Nunjucks というテンプレートエンジンの記法で、ファイルを作成
  • gulp-nunjucks-render という gulp のプラグインを使って、HTML を出力
    • gulp-data という微妙な名前のも併用

という方法で実現した。

技術の選定

全体的な話

そもそも、やりたいことを実現するものの、一般的な名称が分からず、ググッて情報を得るのに若干手こずった。色々ググった結果、やりたいことを実現するには、テンプレートエンジンを使った方法が一般的らしいということが分かった。

テンプレートエンジン?そう。JavaScript とか web フレームワークで HTML を生成するあれ。ただ、HTML 作成を仕事としているフロントエンド界隈の人たちの間では、JavaScript が馴染みがあるようで、JavaScript のテンプレートエンジンを使った方法が多かった。

でも、やりたい事は JS から HTML を生成することじゃなくて、HTML ファイルを生成すること。なので、JavaScript のテンプレートエンジン単体ではなくて、それを gulp から使えるようにする  gulp-xxx というのを使うと良さそう。(元々使っていた gulp-ect の方法もそうだし。)

テンプレートエンジンの選定

テンプレートエンジンと言っても色々あるが、

  1. 機能的にそこそこ充実してること
  2. メンテされていること
  3. HTML ベースであること(HAML のような独自記法(っていうのか?)ではないこと)

を条件とした。

以下、色々なテンプレートエンジンと、NG 理由を簡単に記載する。

  • Mustache (gulp-mustache) → logicless な分、機能が少ない
  • Handlebars (gulp-handlebars etc.) → Mustache と同様
  • Dust.js (gulp-dust etc.)→ 検討するのを忘れてた・・・
  • Pug (gulp-pug, Pug の昔の名前は Jade) → 独自記法
  • Twig.js (gulp-twig) → 特に不可はないが、Nunjucks の方が star 数が多かったので
  • ECT (gulp-ect) → メンテされてない

gulp-xxx の選定

フロントエンド界隈に詳しくないので最初は分からなかったけど、テンプレートエンジンを gulp から使うためには gulp-xxx というのを使うんだけど、同じことをするモジュールがいくつかある。Nunjucks の場合も

  • gulp-nunjucks
  • gulp-nunjucks-render

というのがあった。後者は前者の fork ?っぽいので、gulp-nunjucks-render を使うことにした。

やったこと

前準備

NodeJS やら NPM やら Gulp は、適当にインストールして下さい。

gulp-nunjucks-render のインストール

これも npm install でおなしゃす。

HTML を *.njk に分割

拡張子は慣用的に *.njk とつけるっぽい。今回は、以下のようなファイル・ディレクトリ構成にした。

  • dest: 出力先
  • src: HTMLを生成するための部品
    • layouts/default.njk: レイアウトファイル
    • partials/: 共通部品
      • common: 各言語共通
        • ga.njk: Google Analytics のトラッキングコード
        • header:njk: ヘッダー(サイトのロゴとか)
      • en: 英語
        • head.njk: <head></head> の中身
        • footer.njk: フッター
      • ja: 日本語
        • head.njk
        • footer.njk
    • main: 各ページのファイル
      • en: 英語
        • page1.njk
        • page2.njk
      • ja: 日本語
        • page1.njk
        • page2.njk

各 *.njk ファイルの中身は、後ほど説明する。

gulp タスク

こんな感じ。

var gulp         = require('gulp');
var nunjucks     = require('gulp-nunjucks-render');
var prettify     = require('gulp-prettify');
var data         = require('gulp-data');

var paths = {
   'nunjucks': {
       'srcRoot': '/path/to/root/src/',
       'src': '/path/to/root/src/main/**/*.njk',
       'dest': '/path/to/root/dest',
   }
}
// 説明は後述
function getDataForFile(file) {
  return {
    file: file
  };
}

gulp.task('nunjucks', function() {
    gulp.src(paths.nunjucks.src)
        .pipe(data(getDataForFile)) // 説明は後述
        .pipe(nunjucks({path: paths.nunjucks.srcRoot}))
        // 必要に応じて prettify とか beautify とか入れる
        //.pipe(prettify({
        //  indent_size  : 4,
        //  extra_liners : ''
        //}))
        .pipe(gulp.dest(paths.nunjucks.dest));
});

あとは、gulp タスクを実行すれば、HTML が生成される。

*.njk の書き方(Nunjucks の使い方)

色んな機能があるが、今回使ったもののみを記述。

layout から、各種部品を include する。

layouts/default.njk は以下の通り。

<!DOCTYPE HTML>
<html lang="{{ lang }}">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
 {% include "partials/" + lang + "/head.njk" %}
</head>
<body>
{% include "partials/common/ga.njk" %}
<div class="l-container">
 {% include "partials/common/header.njk" %}
 {% block content %}{% endblock %}
 {% include "partials/" + lang + "/footer.njk" %}
</div>
</body>
</html>

見ればだいたい何をやっているか分かると思うが、include で、各種部品を include している。

include の際の基準となるパスは、gulp-nunjucks-render に path パラメーターとして渡した値となる。今回の gulp タスクの場合は、paths.nunjucks.srcRoot が基準のパスとなる。

その他、{% %} の中は、普通に JavaScript が使えるので、

    {% include "partials/" + lang + "/footer.njk" %}

みたいなのも普通に使える。

block と extends

page1.njk や page2.njk などは、以下のようになっている。

{% set lang = "ja" %}
{% set title = "タイトル" %}
{% extends "layouts/default.njk" %}
{% block content %}
  ページの中身
{% endblock %}

set はその名の通り変数の設定。設定された変数は、include された部品の中でも有効となる。

ちょっと分かりにくいのは、block と extends の2つ。まずは、上の方の layouts/default.njk を見ると

{% block content %}{% endblock %}

という部分がある。layouts/default.njk では、この部分は空欄だが、それを継承(extends)した page1.njk では、その部分の content として 「ページの中身」という文章を定義している。

プログラミング言語に例えれば、layouts/default.njk は、抽象クラスで、page1.njk はそれを継承して content の実装を提供している、という感じ。

“data” を渡す

今回、gulp-data というのも使っている。これは何をするものかというと、gulp タスクによって現在処理中の njk ファイルに関する情報を、gulp のパイプラインに追加する、といったもの。うまく説明出来ないので、詳細はドキュメントを参照。

これをどこで使っているかというと、head.njk で 以下のようなところで使っている。

<meta property="og:url" content="https:/example.com/{{ file.relative | replace(".njk", ".html") }}">

生成される HTML は以下のとおり。

<meta property="og:url" content="https:/example.com/en/page1.html">

file には、現在処理中の njk ファイル(例えば src/main/en/page1.njk )に関する情報が入っていて、それをテンプレートの中に埋め込むことが出来る。

まとめ・感想

Nunjucks は、Mozilla の人たちによって開発されている JavaScript のテンプレートエンジン。それを gulp タスクから使うことで、静的 HTML ファイルをモジュール化することが出来る。

感想としては、一度仕組みを作ってしまえば非常に便利。HTML ファイルの直し忘れとかが減るのでおすすめ。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です