このページではHTMLを生成するJavaScript製テンプレートエンジンのPugについて解説します。
※Pugは元々Jadeという名称でしたが2016年3月、Pugという名称に変更されました。

目次

1. テンプレートエンジンによるテンプレート作成
2. include(インクルード)
3. extends(継承)
4. mixin(ミックスイン)
5. まとめ

1. テンプレートエンジンによるテンプレート作成

Pug(旧称Jade)の使い方

こちらの記事ではJade改めPugの基本的な使い方を紹介しました。
今回はさらにメリットを活かすことができるinclude(インクルード)とextends(継承)、mixin(ミックスイン)について説明します。

HTML、CSS、JavaScriptの拡張言語(メタ言語)の紹介

こちらでも説明している通り、HTMLのみでは複数にまたがるページで、共通部分を管理するのが少々手間となります。Pugを始めとするテンプレートエンジンではそれが格段に楽になります。

Pugでは、include(インクルード)とextends(継承)、mixin(ミックスイン)を使って、HTMLのテンプレートを作ってゆきます。
以下で、一つ一つ説明してゆきたいと思います。

2. include(インクルード)

まずは以下のファイルを同じディレクトリに作成しましょう。

■page1.pug
doctype
html(lang='ja')
  head
    meta(charset='utf-8')
    title Hello, Pug include

  body
    div.content
    p ページ1 コンテンツ

    include footer.pug
■page2.pug
doctype
html(lang='ja')
  head
    meta(charset='utf-8')
    title Hello, Pug include

  body
    div.content
      p ページ1 コンテンツ

    include footer
■footer.pug
div.footer
  p フッター

page1.pug、page2.pugをそれぞれコンパイルします。
ファイル名を続けて記載することでまとめてコンパイルできます。

pug page1.pug page2.pug --pretty

すると、footer.pugの内容が読み込まれたhtmlが生成されます。

■page1.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Hello, Pug include</title>

  <body>
    <div class="content">
      <p>ページ1 コンテンツ</p>
    <div>


    <div class="footer">
      <p>フッター</p>
    </div>
  </body>
</html>
■page2.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Hello, Pug include</title>

  <body>
    <div class="content">
      <p>ページ2 コンテンツ</p>
    <div>


    <div class="footer">
      <p>フッター</p>
    </div>
  </body>
</html>

SSIやサーバーサイド言語でのincludeを知っている方であれば説明いらずですが、各ページでフッターを共通化したい場合、footer.pugというファイルを作っておいて、page1.pug、page2.pugのそれぞれからfooter.pugを読み込む(includeする)ことで、共通部分の管理を楽にすることができます。
includeを使いこなすだけで、複数ページにまたがるWebサイトの構築が圧倒的に楽になります。

includeは

include footer.pug

と記述することで、その記述箇所に、指定のファイルの内容が読み込まれます。page2.pugの例のように、ファイル名指定の際は拡張子.pugを省略しても問題なくコンパイルすることができます。

また、includeするファイルはincludesディレクトリなどにまとめておくことが一般的です。その際には、includeに続けて相対パスでファイルを指定します。

include includes/footer

includeするファイルはpugファイル以外も指定できます。その場合、単純なテキストデータとしてファイルの中身が読み込まれます。

■page1.pug
doctype
html(lang='ja')
  head
    meta(charset='utf-8')
    title Hello, Pug include

  body
    div.content
      p ページ1 コンテンツ

    include includes/footer.php
■footer.php
<?php $text="PHPフッター"; ?>
<div class="footer">
  <p><?php echo $text; ?></p>
</div>
■page1.html(コンパイル後)
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Hello, Pug include</title>

  <body>
    <div class="content">
      <p>ページ1 コンテンツ</p>
    <div>


    <div class="footer">
      <p>PHPフッター</p>
    </div>
  </body>
</html>

3. extends(継承)

includeだけでもテンプレートエンジンの効力は十分なのですが、extends(継承)を使用するとさらに効率よくWebサイトを構築することが可能です。

まずは以下ファイルを作成しましょう。index.pugと同じ階層にlayoutディレクトリを作り、その中にlayout.pugを作成します。

■layout/layout.pug
doctype html
html
  head
    meta(charset='utf-8')

    block title

  body
    block content
      h1 Default Content
■index.pug
extends layout/layout.pug

block title
  title Page Title

block content
  h1 Page Content

index.pugファイルをコンパイルすると以下のようになるかと思います。

■index.pug
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Page Title</title>

  <body>
    <h1>Page Content</h1>
  </body>
</html>

先ほどのincludeとの違いが少しわかりづらいかと思います。
以下の図のように、includeでは共通部分をパーツ化していましたが、共通部分が多くなってきた場合、それらをまとめて一つにした方が管理しやすくなります。
その際にextendを使用し、共通部分を一つのファイルにし、ページごとに異なる部分をそれぞれのページで記述する形になります。

 

■include

■extends

はじめにページのテンプレートとして、layout.pugで基本的な構造を作成します。その中でページごとに変更したい箇所は、block文を使用します。
今回はhead内のタイトルとbody内のコンテンツにblock文を使用しました。
block文の書き方は、blockに続いてブロック名を宣言します。

■layout/layout.pug
block title

ここではtitleにしていますが、なんでも構いません。内容に沿ったわかりやすいブロック名がいいでしょう。

index.pug側で、個別のタイトルの内容を作成します。
まず、index.pugではどのファイルをテンプレートとして使用するかを、extendsで指定します。

■index.pug
extends layout/layout.pug

そして、先ほどブロック名を宣言したブロックの内容を定義します。
この時、layout.pugでの宣言時と同じblock文を使用するため紛らわしいのですが、こちらではインデントで区切って内容を定義する必要があります。

■index.pug
block title
  title Page Title

block文は初期(デフォルト)値を持つことができます。先出の例ではコンテンツ部分で使用しています。初期値はblock文に続く行に、一つ下のインデントで記述します。
読み込み側(ここではindex.pug)で内容が定義されていないと、初期値が表示されます。

■layout/layout.pug
block content
  h1 Default Content

index.pugでblock contentを定義しないと、出力されるHTMLは以下のようになります。

■index.html
<h1>Default Content</h1>

4. mixin(ミックスイン)

最後に、mixinについて説明します。
ここまでの内容で基本的なテンプレートは作成できますが、includeで共通部分を作る際に、ページごとに内容が少しずつ違う場合はどのようにしたらいいでしょうか。
例えばグローバルナビゲーションを共通化した際に、今いるページには印(クラス)をつけたい場合などが当てはまります。

まずはincludeと同じように、全く同じ内容を複数箇所で呼び出してみましょう。

■index.pug
mixin list
  ul.gNav
    li: a(href='home.html') HOME
    li: a(href='about.html') ABOUT
    li: a(href='news.html') news

doctype html
html
  head
    title Hello, Pug Mixins

  body
    +list

    .content ページごとのコンテンツ

    +list
■index.html(コンパイル後)
<!DOCTYPE html>
<html>
  <head>
    <title>Hello, Pug Mixins</title>
  </head>
  <body>
    <ul class="gNav">
      <li><a href="home.html">HOME</a></li>
      <li><a href="about.html">ABOUT</a></li>
      <li><a href="news.html">news</a></li>
    </ul>
    <div class="content">ページごとのコンテンツ</div>
    <ul class="gNav">
      <li><a href="home.html">HOME</a></li>
      <li><a href="about.html">ABOUT</a></li>
      <li><a href="news.html">news</a></li>
    </ul>
  </body>
</html>

mixin(ミックスイン)は、mixin文に続きmixin名を宣言し、次のインデントで内容を定義します。
mixinを呼び出す時には、+のすぐ後にmixin名を指定します。

mixinはincludeと異なり、同じファイルに内容を記述することができます。また、mixinをまとめたファイルを新たに作成し、そのファイルをincludeして使うことも可能です。

■mixin/mixin.pug
mixin list
  ul.gNav
    li: a(href='home.html') HOME
    li: a(href='about.html') ABOUT
    li: a(href='news.html') NEWS
■index.pug
include mixin/mixin

doctype html
html
  head
    title Hello, Pug Mixins

  body
    +list

    .content ページごとのコンテンツ

    +list

mixinは関数のイメージで考えるとわかりやすいかと思います。

それではページ毎にグローバルナビゲーションの現在のページにクラスをつけてゆきましょう。

■mixin/mixin.pug
- var list = ['home', 'about', 'news']

mixin list(now)
  ul.gNav
    each item in list
      - var name = item.toUpperCase()

      if now === item
        li.now: a(href=item + '.html') #{name}
      else
        li: a(href=item + '.html') #{name}
■index.pug
include mixin/mixin

doctype html
html
  head
    title Hello, Pug Mixins

  body
    +list('home')

    .content ページごとのコンテンツ

    +list('home')
■index.html(コンパイル後)
<!DOCTYPE html>
<html>
  <head>
    <title>Hello, Pug Mixins</title>
  </head>
  <body>
    <ul class="gNav">
      <li class="now"><a href="home.html">HOME</a></li>
      <li><a href="about.html">ABOUT</a></li>
      <li><a href="news.html">NEWS</a></li>
    </ul>
    <div class="content">ページごとのコンテンツ</div>
    <ul class="gNav">
      <li class="now"><a href="home.html">HOME</a></li>
      <li><a href="about.html">ABOUT</a></li>
      <li><a href="news.html">NEWS</a></li>
    </ul>
  </body>
</html>

コードをスッキリさせるためにeach inを使用したので少しわかりづらいですが、ポイントはmixin側でも呼び出し側でも新たに括弧の中にパラメータが加えられているところになります。

mixin側では任意の変数名を括弧の中に入れます。

mixin list(now)

呼び出し側では、渡す値を入れます。

+list('home')

これによりlistmixinにはhomeという文字列が渡され、変数nowの中に格納されます。
変数nowはif文によって評価され、ナビゲーションリストの内容と同じだった場合、li要素にnowクラスが付与されるようになっています。

パラメータはJavaScriptの関数と同じように、カンマ区切りで複数やり取りすることが可能です。

■mixin側
mixin list(now, name)

+list('home', 'INDEX')

5. まとめ

以上、Pugでinclude(インクルード)とextends(継承)、mixin(ミックスイン)を使用する方法を紹介してきました。
実際にはこれら3つを組み合わせて、サイトのテンプレートを作成してゆくことになります。

今回はlayout.pugやmixin.pugなど、レイアウトテンプレートやmixinを一つのファイルにまとめましたが、layout-header.pug、layout-footer.pugを作成し、layout.pugでそれらをincludeしたり、mixin-grovNav.pug、mixin-sidebar.pugと複数のmixinファイルを用意し、必要なものだけ読み込んだりして構築してゆくことも可能です。

普段プレーンなHTMLだけでコーディングしていると、extendsやmixinがとても難しく感じますが、一つ一つできるところから取り入れてゆくことで少しずつ効率化してゆくのが良いかと思います。
全てマスターした頃には、プレーンなHTMLだけでコーディングしていた頃より圧倒的なスピードで開発できるようになっているはずです。