みなさんは、大量にある静的Webサイトを制作するとき、どうしていますか?

昔だと、Dreamweaverのテンプレート機能を利用していたかもしれません。
最近、もりさんは、Node.jsgulpで制作しています。

最初は、Sassimageminのついでに、使っていましたが、テンプレートエンジンで生成すると、間違いが少ないので、気に入りました。

Swigの選択について

EJSというテンプレートエンジンも有名だったので、迷いました。

LaravelのBladeやSymfonyのTwigなどのPHPフレームワークのテンプレートエンジンが頭にあったので、Swigというテンプレートエンジンを採用してみました。

gulpでgulp-swigを選択

gulpで使いたかったので、gulp-swigを選択しました。 もしかしたら、gulpと普通のSwigでもできるかもしれませんが、試していません、すみません。

swig

gulp-swig

前提の条件

すでにNode.jsをインストールしている状態で始めます。 また、npm initnpm install –save-dev package_nameなどは、適宜お願いします。

階層

階層は、以下ようなイメージで進めます。

project
├ app(制作データ)
│├ template(template用html)
││└ template.html
│├ next(下階層ディレクトリ)
││└ index.html(中ページ)
│└ index.html(トップページ)
└ dest(納品データ)

Swigを利用したサンプルデータ

TwigBladeを使ったことがあれば、それほど迷うことはないと思います。 もりさんは、以下のサイトで調べることが多いです。

Swig – A Node.js and Browser JavaScript Template Engine

各ファイルの中身の例

だいたい、こんな感じになるのではないでしょうか。

project/app/template/template.html

{% set site_name = 'サイト名' %}
{% set site_description = 'サイト名の説明' %}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>{% block title %}{{ site_name }}{% endblock %}</title>
    <meta name="description" content="{{ site_description }}">
    <link rel="stylesheet" href="{{ path }}css/style.css">
    <script src="{{ path }}js/plugin.js"></script>
    {% block javascript %}{% endblock %}
</head>
<body>
    {% block content %}
    {% endblock %}
</body>
</html>

project/app/index.html

{% extends "./template/template.html" %}
{% set path = "./" %}
{% block title %}
    {% parent %} - top
{% endblock %}
{% block javascript %}
    <script src="{{ path }}js/top.js"></script>
{% endblock %}

{%
  set news = [
    {'date':'2017/08/28', 'title': 'タイトル1'},
    {'date':'2017/08/29', 'title': 'タイトル2'},
    {'date':'2017/08/30', 'title': 'タイトル3'},
    {'date':'2017/08/30', 'title': 'タイトル4'}
  ];
%}

{% block content %}
<div class="container">
    {% for n in news %}
    <section class="container__group">
        <h1>{{ n.title }}</h1>
        <p>{{ n.date }}</p>
    </section>
    {% endfor %}
</div>
{% endblock %}

Swigの使い方

ここに書いたTagだけで、ほとんどのことはできると思います。

変数の展開とフィルター

変数をテンプレート内に展開するためには、以下のように書きます。

{{ path }}
{{ n.title }}
{{ n['date'] }}

また、変数に、フィルターを掛けて、変更することができます。 パイプ文字を繋げて、複数のフィルターをまとめて設定することもできます。

{{ stuff | striptags }}

Swig » Documentation » Filters

extends

テンプレートを読み込みます。 extendsで、大枠になる親テンプレートを読み込んで、部分的に書き換えたい箇所を、変数やBlockで書き換えていきます。

{% extends "./template/template.html" %}

block

親テンプレートで、blockを定義していると、後から上書きすることができます。

{% block content %}...{% endblock %}

set

変数を定義します。 すでに設定されている場合は、上書きになります。

{% set foo = 'anything' %}

if

条件分岐できます。

{% if obj == 'a' %}
<p>a</p>
{% elseif obj == 'b' %}
<p>b</p>
{% else %}
<p>その他</p>
{% endif %}

for

オブジェクトや配列をループします。 繰り返すコンテンツなどを生成することができます。 forの中では、loop.indexなどの変数にアクセスできます。

loop.index現在のindex(1から始まるindex)
loop.index0現在のindex(0から始まるindex)
loop.revindex終わりから始まる現在のindex(1から始まるindex)
loop.revindex0終わりから始まる現在のindex(0から始まるindex)
loop.keyオブジェクトの場合、現在のkeyを取得できます。配列の場合、loop.indexと同じになります。
loop.firstloopの最初であれば、Trueになります。
loop.lastloopの最後であれば、Trueになります。
{% for n in news %}
<section class="container__group">
    <h1>{{ n.title }}</h1>
    <p>{{ n.date }}</p>
</section>
{% endfor %}

include

別ファイルのコンテンツを読み込むことができます。 その時、一緒に変数を入れることも可能です。

{% include "./include.html" with obj only %}

parent

親テンプレートのblockを継承して、現在のblockに追加できます。

{% block title %}
    {% parent %} - top
{% endblock %}

gulp-swigの問題

実は、このまま書き出しを行うと、いくつか問題が発生しました。

  • 変数などを定義したソースが、そのまま空いてしまう
  • テンプレートだけを更新した場合、書き出しに工夫が必要
  • htmの拡張子が、htmlになってしまう

そのあたりを考慮して、gulpfile.jsとgulpのプラグインを設定します。

変数などを定義したソースが、そのまま空いてしまう

一度、HTMLを圧縮し、それを整形して、余計な空きをなくします。 そのために以下のプラグインを導入します。

gulp-html-minifierHTMLを圧縮します。
gulp-prettifyHTMLを整形します。

テンプレートだけを更新した場合、書き出しに工夫が必要

gulp-changedで変更を監視し、Browsersyncで自動更新している場合、テンプレートを更新しても、変更を検知しないので、反映されません。

gulp-ifを利用して、テンプレートを更新したときは、変更を検知せず全て書き出しを行います。

また、Swigのcacheをfalseにします。

gulp-if条件分岐でフローを制御します。

htmの拡張子が、htmlになってしまう

普通はhtmを使うことなどないと思いますが、既存のコンテンツの追加修正で、htmのファイルのままでいたいのに、htmlになってしまいます。

gulp-renameを利用して、拡張子をhtmに戻します。

gulp-renameファイル名変更します。

できたgulpfile

gulpfile.jsの中身は、こんな感じです。 できるだけ、Swigに関係しているものだけ切り取っています。

gulpfile.js

var
gulp = require('gulp'),
rename = require('gulp-rename'),
gulpif = require('gulp-if'),
browser = require('browser-sync'),
plumber = require('gulp-plumber'),
changed = require('gulp-changed'),
runSequence = require('run-sequence'),
swig = require('gulp-swig'),
prettify = require('gulp-prettify'),
htmlmin = require('gulp-html-minifier');

var paths = {
    swig :['app/**/@(*.html|*.htm)','!app/template/**'],
    dist :'./dest'
};
var watch = {
    swig  :paths.swig,
    layout:'app/template/**'
};

var layoutCompile;

gulp.task('compile', function () {
    var flag = false;
    return gulp
    .src(paths.swig)
    .pipe(plumber())
    .pipe(gulpif(!layoutCompile, changed(paths.dist)))
    .pipe(rename(function (path) {
        if (path.extname === '.htm'){
            flag = true;
        }else{
            flag = false;
        }
    }))
    .pipe(swig({
        defaults: { cache: false }
    }))
    .pipe(rename(function (path) {
       if (flag){
            path.extname = '.htm';
       }
    }))
    .pipe(htmlmin({collapseWhitespace: true}))
    .pipe(prettify({indent_size: 4}))
    .pipe(gulp.dest(paths.dist))
    .pipe(browser.reload({stream:true}));
});

gulp.task('watchSwig', function() {
    layoutCompile = false;
});

gulp.task('watchLayout', function() {
    layoutCompile = true;
});

gulp.task('watch', function() {
    gulp.watch(watch.swig, ['watchSwig', 'compile']);
    gulp.watch(watch.layout, ['watchLayout', 'compile']);
});

gulp.task('server', function() {
    browser({server: {baseDir: paths.dist}});
});

gulp.task('default', function(callback) {
    layoutCompile = false;
    runSequence(
        'compile',
        'server',
        'watch',
        callback
    );
});

まとめ

これで、100ページくらいならさくっと作れます。
ただ、ページ数が多くなると、テンプレートを更新する場合、時間がかかるようになります。
個別にページを保存して確認し、タイミングで一気に書き出すようにしましょう。

現場のプロが教えるWeb制作の最新常識[アップデート版] (知らないと困るWebの新ルール)
久保 知己 酒井 優 塚口 祐司 前川 昌幸
エムディエヌコーポレーション (2016-06-01)
売り上げランキング: 49,123