GulpでテンプレートエンジンのPugを使ってみた

技術情報

Getting Started – Pug

前回はGulpで利用するテンプレートエンジンのEJSをご紹介いたしました。

GulpでテンプレートエンジンのEJSを使ってみた
EJS - JavaScript Templates 最近、静的なテンプレートを生成する方法としていろいろ試行錯誤していたのですが、JavaScriptで静的なhtmlを生成できるEJSというものを知り、使ってみることにしました。 EJSと...

EJSの他に、Gulpで利用できるテンプレートエンジンにはPugというものが有名なようなので、試しに使ってみました。

Pugとは

Pug(旧Jade)はHTMLを書くためのテンプレートエンジンで、JST (JavaScript Templates) の一つです。Pugは「.pug」という拡張子のファイル内でHTMLのようにタグで囲まず、改行やインデントによってページを作っていきます。Pugを利用するには「Node.js」が必要になります。PugにはGulp用のPugがあり、Gulpで使うことができます。

Pugで何ができるか

  • ループや条件分岐。
  • 共通ファイルの読み込み(インクルード)やパラメータの引き渡し。
  • JavaScriptと同様に、数、文字列、配列、オブジェクトなどを扱える。
  • htmlファイルへの出力。

gulp-pugを導入する

Pugを利用するには「Node.js」が必要なので前もってインストールしておきます。「Node.js」の導入は「Node.jsを導入する」を参照してください。

gulp-pugをインストールする

今回、PugはGulpで利用するので、下記のコマンドを入力してgulp-pugをインストールします。

npm i -D gulp-pug

gulpfile.jsに下記の記述をし、pugを利用できるようにします。

var pug = require("gulp-pug");

gulpfile.jsにgulp-pugのタスクを記述する

次にgulpfile.jsにgulp-pugのタスクを記述します。

//Pug(テンプレートエンジン)
gulp.task("pug", function() {
    gulp.src(
       ["src/pug/**/*.pug",'!' + "src/pug/**/_*.pug"] //参照するディレクトリ、出力を除外するファイル
    )
    .pipe(pug())
    .pipe(gulp.dest("dest/")) //出力先
});
  • 「”src/pug/**/*.pug”」はサブディレクトリも含めた「.pug」ファイルを参照。
  • 「’!’ + “src/pug/**/_*.pug”」はサブディレクトリも含めた「_」が先頭についた「.pug」ファイルの出力(インクルードファイル)を除外。
  • 「.pipe(gulp.dest(“dest/”))」で「dest」というディレクトリ以下にファイルを出力。

Pugを使う

Pugファイルを作成する

作業するディレクトリに手動でPugファイルを作成します。今回は「src/pug/」にindex.pugを作成しました。

Pug 「src/pug/」に「index.pug」
「src/pug/」に「index.pug」

Pugの記法

index.pugには下記のように記述しました。

doctype html
html(lang="ja")
  head
    meta(charset="utf-8")
    title トップページタイトル
    link(rel="stylesheet" href="css/style.css")
  body
    h1 トップページ
    main
      p トップページテキスト

Pugは基本的に一行で記述します。doctype htmlで宣言、改行とインデントで要素をネスト、要素の後ろに半角スペースで要素内のテキストとなります。

htmlとして出力する

htmlとして出力するには「gulpfile.js」がある階層で下記のコマンドを入力してhtmlを出力します。

gulp pug

すると、gulpfile.jsに記述した「.pipe(gulp.dest(“dest/”))」が動作し、「dest/」直下にindex.htmlが出力されます。

Pug 「dest/」直下に「index.html」
「dest/」直下に「index.html」

gulpやPugのバージョンにもよりますが、「base」プロパティを指定せずとも「src/pug/news/」にindex.pugと作成した場合、階層はそのままコピーされ、「dest/」に「news/index.pug」が出力されます。ディレクトリ階層がそのままコピーされない場合は、「base」プロパティを使用しましょう。

Pug ディレクトリ階層がコピーされる
ディレクトリ階層がコピーされる

出力したhtmlは下記のようになります。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>トップページタイトル</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <h1>トップページ</h1>
        <main>
            <p>トップページテキスト</p>
        </main>
    </body>
</html>

Pugで出力するhtmlはがデフォルトでminify化されるので上記のソースは整形しています。

共通ファイルをインクルードする

ヘッダーやフッターなどの共通部分をinclude文を利用して、複数のページで共通で使用することができます。

_header.pug(ヘッダー)

今回は「/src/pug/common/」に「_header.pug」として共通で使うヘッダーを作成しました。

Pug 「/src/Pug/common/」に「_header.pug」
「/src/Pug/common/」に「_header.pug」

ヘッダーの内容は下記のようにしました。

<header>
    ヘッダー
</header>

_footer.pug(フッター)

今回は「/src/pug/common/」に「_footer.pug」として共通で使うフッターを作成しました。

Pug 「/src/pug/common/」に「_footer.pug」
「/src/pug/common/」に「_footer.pug」

フッターの内容は下記のようにしました。

<footer>
    フッター
</footer>

index.pugにヘッダーとフッターをインクルードする

index.pugに下記のように記述します。

doctype html
html(lang="ja")
  head
    meta(charset="utf-8")
    title トップページタイトル
    link(rel="stylesheet" href="css/style.css")
  body
    include common/_header.pug
    h1 トップページ
    main
      p トップページテキスト
    include common/_.pug
  • 「include common/_header.pug」でヘッダーをインクルード。
  • 「include common/_footer.pug」でフッターをインクルード。

上記のインクルードを行った後、gulp pugコマンドを入力して、index.htmlの中身を見ると下記のようになります。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>トップページタイトル</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <header>ヘッダー</header>
        <h1>トップページ</h1>
        <main>
            <p>トップページテキスト</p>
        </main>
        <footer>フッター</footer>
    </body>
</html>

index.pugにインクルードしたヘッダーとフッターがindex.htmlに出力されています。

「src/pug/news/index.pug」に上記のインクルードを行う場合は、階層が一つ変わるので下記のように「../common/」となります。

  • 「include ../common/_header.pug」
  • 「include ../common/_footer.pug」

ifやforなど

Pugはifやforで判定や繰り返しなどを行うことができます。

if文

「- var」が変数となります。

- var myFlg = 1
if myFlg == 1
  p 1番目
else if myFlg == 2
  p 2番目
else
  p 3番目

「if myFlg == 1」によって1番目の要素が出力されます。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>トップページタイトル</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <header>ヘッダー</header>
        <h1>トップページ</h1>
        <main>
            <p>トップページテキスト</p>
            <p>1番目</p>
        </main>
        <footer>フッター</footer>
    </body>
</html>

記述した条件での要素の表示は下記のようになります。

Pug if文を出力
if文を出力

for文

ifの時と同じようにfor文の前に半角ハイフンを記述します。中かっこは「{」で始め、「- }」で終わります。

ul
  - for (var i = 1; i < 6; i++) {
    li #{i}個目のli
  - }

forで繰り返した分だけliが出力されています。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>トップページタイトル</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <header>ヘッダー</header>
        <h1>トップページ</h1>
        <main>
            <p>トップページテキスト</p>
            <ul>
                <li>1個目のli</li>
                <li>2個目のli</li>
                <li>3個目のli</li>
                <li>4個目のli</li>
                <li>5個目のli</li>
            </ul>
        </main>
        <footer>フッター</footer>
    </body>
</html>

記述した条件でのliの表示は下記のようになります。

Pug for文を出力
for文を出力

jsonでタイトルやディスクリプションを出力する

jsonファイル

Pugはjsonを利用してtitleやdiscriptionの内容を出力させることができます。まずは下記のような内容でjsonを作成します。

{
    "pages": {
        "index": {
            "id": "index",
            "title": "トップページタイトル",
            "description": "トップページディスクリプション"
        },
        "news-index": {
            "id": "news-index",
            "title": "ニュースページタイトル",
            "description": "ニュースディスクリプション"
        }
    }
}

jsonファイルは「pages.json」として下記の階層に設置しました。

Pug 「pages.json」を設置
「pages.json」を設置

jsonをパースできるように下記の記述をgulpfile.jsに追記します。

var fs = require("fs");

gulp-data

Pugで外部のデータを扱うため、下記のコマンドを入力してgulp-dataをインストールします。

npm i -D gulp-data

インストールしたgulp-dataを使用するためにgulpfile.jsに下記の記述をします。

var data = require("gulp-data");

gulp.taskにpages.jsonを追加

作成したpages.jsonをPugに読み込むため、「return JSON.parse(fs.readFileSync(`./pages.json`));」を追記します。

gulp.task("pug", function () {
    gulp.src(
        ["src/pug/**/*.pug",'!' + "src/pug/**/_*.pug"] //参照するディレクトリ、出力を除外するファイル
    )

    //追記
    .pipe(data( file => {
        return JSON.parse(fs.readFileSync(`./pages.json`));
    }))
    //ここまで追記

    .pipe(pug())
    .pipe(gulp.dest("dest/")) //出力先
});

index.pugにタイトルとディスクリプションの変数タグを埋め込む

index.pugの先頭にページを判定する変数、titleとdescription要素に変数タグを記述します。

- var pageId = "index"
doctype html
html(lang="ja")
  head
    meta(charset="utf-8")
    title #{pages[pageId].title}
    meta(name="description" content=pages[pageId].description)
    link(rel="stylesheet" href="css/style.css")
  body
    include common/_header.pug

    h1 トップページ
    main
      p トップページテキスト

    include common/_footer.pug
  • 「- var pageId = "index"」:ページ判定。
  • 「#{pages[pageId].title}」:タイトル変数。
  • 「pages[pageId].description」:ディスクリプション変数。

上記の変数タグを埋め込んだ後、gulp pugコマンドを入力して、index.htmlの中身を見ると下記のようになります。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>トップページタイトル</title>
        <meta name="description" content="トップページディスクリプション">
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <header>ヘッダー</header>
        <h1>トップページ</h1>
        <main>
            <p>トップページテキスト</p>
        </main>
        <footer>フッター</footer>
    </body>
</html>

jsonで記述したタイトルとディスクリプションが出力されていることがわかります。

ニュース用のタイトルやディスクリプションを埋め込む場合は下記のようになります。

- var pageId = "news-index"
doctype html
html(lang="ja")
  head
    meta(charset="utf-8")
    title #{pages[pageId].title}
    meta(name='description' content=pages[pageId].description)
    link(rel="stylesheet" href="css/style.css")
  body
    include ../common/_header.pug

    h1 ニュースページ

    main
      p ニュースページテキスト

    include ../common/_footer.pug

pageIdがニュース用のものになり、タイトルとディスクリプションの変数は変わりません。出力した「news/index.html」の中身は下記のようになります。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>ニュースページタイトル</title>
        <meta name="description" content="ニュースディスクリプション">
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <header>ヘッダー</header>
        <h1>ニュースページ</h1>
        <main>
            <p>ニュースページテキスト</p>
        </main>
        <footer>フッター</footer>
    </body>
</html>

インクルードファイル内で相対パスを取得する

ヘッダーやフッターなどをインクルードしている場合、インクルード内で記述しているa要素などのパスを取得したいと思います。方法としてはいろいろありますが、ここでは「pug」ファイルにパスを記述し、変数に渡して相対パスを取得します。

- var relativePath = "./"

「index.pug」に上記の記述をします。「news/index.pug」であれば下記のようになります。

- var relativePath = "../"

インクルードファイルの「_header.pug」には下記のように記述します。

header ヘッダー
  a(href=`${relativePath}`) トップページ

上記の上記の「href」内の「`${relativePath}`」でパスが返ります。記述後にgulp pugを入力して出力します。「index.html」と「news/index.html」の「トップページ」のパスは下記のようになります。

<header>ヘッダー
    <a href="./">トップページ</a>
</header>

上記、「index.html」の「トップページ」のパスは「./」、「news/index.html」から見た場合は下記のように「../」となります。

<header>ヘッダー
    <a href="../">トップページ</a>
</header>

今回の方法は画像やCSSのパスにも利用できます。

img(src=`${relativePath}images/hoge.jpg` width="100" height="100" alt="画像")

記述がわかりづらい要素での記法

picture要素

picture
  source(media="(max-width:400px)" srcset=`${relativePath}images/sp.jpg 400w` sizes="100vw")
  source(media="(max-width:600px)" srcset=`${relativePath}images/tab.jpg 600w` sizes="100vw")
  img(src=`${relativePath}images/pc.jpg` alt="代替")

figureにネストしているpicture要素

figure
  picture
    source(media="(max-width:400px)" srcset=`${relativePath}images/sp.jpg 400w` sizes="100vw")
    source(media="(max-width:600px)" srcset=`${relativePath}images/tab.jpg 600w` sizes="100vw")
    img(src=`${relativePath}images/pc.jpg` alt="代替")
  figcaption 画像キャプション

まとめ

  • Pugは静的なテンプレートエンジン。
  • Pugは専用の記法がある。
  • gulp-pugはGulp用のPug。
  • Pugは基本的にJavaScriptと同様のことができる。

Pugで記述するhtmlは独特の記法ですが、タグで囲う必要がないので楽なのと、一ファイルに記述するコード量を抑えることができます。

スポンサーリンク
技術情報
スポンサーリンク
シェアする
ボヘミアンをフォローする
この記事が気に入ったら
いいね!しよう
最新情報をお届けします。
スポンサーリンク

コメント