テンプレートエンジンPug入門 第1回 Pugの特徴

HTMLを簡単に記述するためのテンプレートエンジン、Pug。その独自の記法を学び、より効率的にHTMLを作成しましょう。

発行

著者 坂巻 翔大郎 フロントエンド・エンジニア
テンプレートエンジンPug入門 シリーズの記事一覧

Pugとは

Pug(パグ)*はHTMLを簡単に記述するためのテンプレートエンジンです。PugではHTMLの階層構造をインデントで表現するという特徴があり、Hamlというテンプレートエンジンの影響を受けています。CSSプリプロセッサであるSassのSass記法もまた、Hamlが元になっています。インデントで表す記法になじみがある方もいるのではないでしょうか。

*注:名前の変遷

ちなみに、PugはもともとJade(ジェイド)と呼ばれていました。Jadeという名前が商標登録されていることがわかったため、議論の結果、プロジェクト名はPugになり、npmのパッケージ名はpugとなりました。

テンプレートエンジンを使うメリット

筆者は、何かしらのテンプレートエンジンを使ってHTMLを作成することがほとんどです。テンプレートエンジンには、PugやMustache、HandlebarsやEJSなどたくさんの種類がありますが、このシリーズでは筆者がよく利用するPugを解説していきます。

テンプレートエンジンを使わないでHTMLを作成する場合、HTML自体にはプログラミング言語でいう変数やfor文などの仕組みがないので、同じような記述を何度もしなくてはいけません。ウェブサイトのヘッダーやフッターの記述をすべてのHTMLに書いたとして、少しだけの修正であっても、すべてのHTMLを触らなくてはいけません。また、HTMLを書いていると閉じタグを書き忘れることや、うっかり消してしまうこともあります。

テンプレートエンジン

テンプレートエンジンについては、次のシリーズでも詳しく解説しています。

Pugは、共通する部分を別のファイルに分けてそれを読み込む形で使うことができます。さらに、レイアウト部分のファイルとそこに入るコンテンツ部分を分けておくこともできます。そして、閉じタグを書く必要がありません。

Pugの記法は独特に思えるかもしれません。ですが、その記法さえ覚えてしまえば、いままでよりも簡単に、より効率的にHTMLを作成できるようになるでしょう。

Pugの記法の特徴

Pugを使った記法を見てみましょう。詳しい方法は次節以降で解説しますので、まずはPugでどう書くのか、そして、書いたものからどのようなHTMLが生成されるかを見てみます。

Pugのテンプレートファイルの拡張子は.pugです。ここではindex.pugとします。

demo-1/index.pug

doctype html
html(lang="ja")
  head
    title タイトル
  body
    h1#heading 見出し
    p.paragraph.
      本文です。本文です。本文です。本文です。本文です。
      本文です。本文です。本文です。本文です。本文です。

PugはHTMLのタグを記述するために<>を書く必要はありません。また開始タグに対応した閉じタグも不要です。HTMLの文書構造はインデントで表現します。id属性値やclass属性はh1#headingp.paragraphと書き、CSSのコーディングでよく利用されるEmmetに似ています。

このPugのテンプレートファイルからHTMLを生成すると、次のようになります。

demo-1/index.html

<!DOCTYPE html><html lang="ja"><head><title>タイトル</title></head><body><h1 id="heading">見出し</h1><p class="paragraph">本文です。本文です。本文です。本文です。本文です。
本文です。本文です。本文です。本文です。本文です。</p></body></html>

Pugを使用すると、普通にHTMLを書くよりも行数が短く、記述する量も少ないことがわかります。

Pugの環境構築

それでは、Pugの使い方を詳しく見てみましょう。まず、環境構築から紹介していきます。なお、この記事では、CLIでPugを実行する方法を使っていきます。

まずは適当なディレクトリを作成して、pug-cliをインストールします。

$ mkdir demo-1 && cd demo-1
$ npm init -y
$ npm install pug-cli -D

package.jsonは次のようになっています。

{
  "name": "demo-1",
  "version": "1.0.0",
  "description": "",
  "muun": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "pug-cli": "^1.0.0-alpha6"
  }
}

インストール後、ヘルプを表示して何ができるかを見てみましょう。

$ npx pug --help

npxを利用する

npxはインストールされていないパッケージをダウンロードしつつ、直接実行する方法として知られていますが、ローカルインストールされたパッケージがあればそちらを優先して使います。このローカルのパッケージのコマンドを探してくれる性質を利用することで、インストールしたパッケージへのPATHが通っていなくても気にせず実行できます。

PugからのHTML生成

次に、Pugのテンプレートファイルを記述します。先ほどのindex.pugを例に見てみましょう。

demo-1/index.pug

doctype html
html(lang="ja")
  head
    title タイトル
  body
    h1#heading 見出し
    p.paragraph.
      本文です。本文です。本文です。本文です。本文です。
      本文です。本文です。本文です。本文です。本文です。

最低限、覚えておいたほうが良い記法を簡単にまとめてみました。

Pug記法 変換後のHTML
h1.heading <h1 class="heading">...
h1#category1 <h1 id="category1">...
h1.heading#category1 <h1 class="heading" id="category1">...
.heading#category1 <div class="heading" id="category1">...

class属性とid属性は、CSSセレクタ同様に記述できます。要素名を省略した場合は、div要素になります。

Pug記法 変換後のHTML
a(href="example.com") <a href="example.com">
a(href="example.com", target="_blank") <a href="example.com" target="_blank">
a(href="example.com" target="_blank") <a href="example.com" target="_blank">

要素の属性値は、()で囲います。複数の属性を記述する場合は、半角スペースもしくは,で区切ります。

PugのテンプレートファイルからHTMLを生成するには、次のコマンドを実行します。

$ npx pug index.pug

そうして生成されたHTMLの内容は、前節でも紹介したように、次のようになります。

demo-1/index.html

<!DOCTYPE html><html lang="ja"><head><title>タイトル</title></head><body><h1 id="heading">見出し</h1><p class="paragraph">本文です。本文です。本文です。本文です。本文です。
本文です。本文です。本文です。本文です。本文です。</p></body></html>

何も指定しなければ、ファイル内の文字列から不要なインデントや改行などが消されて出力されます。もし改行やインデントなどを維持したい場合は、--prettyオプションを指定します。

$ npx pug index.pug --pretty

demo-1/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>タイトル</title>
  </head>
  <body>
    <h1 id="heading">見出し</h1>
    <p class="paragraph">
      本文です。本文です。本文です。本文です。本文です。
      本文です。本文です。本文です。本文です。本文です。
    </p>
  </body>
</html>

pug-cliのコマンド例

上記ではまず単純に、1つのPugのテンプレートファイルから、同じディレクトリにHTMLを生成するコマンドを使いましたが、次のコマンドを利用するとより柔軟に対応できます。

templatesディレクトリ以下にあるPugのテンプレートファイルをすべてHTMLに変換する場合は、次のコマンドを実行します。

$ npx pug templates

templates-Aディレクトリとtemplate-BディレクトリにあるファイルをHTMLにし、htmlディレクトリに生成する場合は、次のコマンドを実行します。

$ npx pug templates-A templates-B --out html

index.pugabout.pugを、HTMLにしたい場合は次のコマンドを実行します。

$ npx pug {index, about}.pug

index.pugabout.pugをHTMLにし、htmlディレクトリに生成する場合は、次のコマンドを実行します。

$ npx pug {index, about}.pug --out html

テンプレートにデータを渡して利用する

Pugでは、テンプレートファイルの中で、別のファイルに定義したデータを参照できます。

まずは、JSONやJSファイルでテンプレートで使用したいデータを定義します。

demo-2/data.json

{
  "title": "デモページ",
  "description":  "これはデモページです",
  "isDemo": true
}

ここでは、JSONでこのページのタイトルであるtitle、サイトの説明となるdescription、デモページである場合はtrueとするisDemoの3つのプロパティを定義しました。

この3つのプロパティを、Pugのテンプレート内では変数として使用します。

demo-2/index.pug

doctype html
html(lang="ja")
  head
    title #{title}
    meta(name="description" content=description)
  body
    h1#heading 見出し
    if isDemo === true
      p.paragraph.-demo.
        デモの本文です。isDemoがtrueであれば表示されます。
    else
      p.paragraph.
        本文です。isDemoがfalseであれば表示されます。

JSONで定義したtitleを、title要素内のテキストとして展開するために#{title}とします。descriptionは、meta要素のcontent属性に指定します。属性値にそのまま変数を指定します。HTMLファイルになる際に、ダブルクオーテーションで囲まれます。そしてisDemoは、if isDemo === trueというようにテンプレート内でその値による条件分岐として使います。

Pugのテンプレートで変数を使用する準備ができたので、HTMLファイルを生成します。pugコマンドの-Oオプション(ObjectO)でJSONファイルのパスを指定します。

$ npx pug -O data.json index.pug --pretty

HTMLが生成されたものは、次のようになります。JSONで定義したそれぞれの値が、HTMLに反映されていることがわかります。

demo-2/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>デモページ</title>
    <meta name="description" content="これはデモページです">
  </head>
  <body>
    <h1 id="heading">見出し</h1>
    <p class="paragraph -demo">デモの本文です。isDemoがtrueであれば表示されます。</p>
  </body>
</html>

data.jsonisDemoプロパティをfalseにして、再度HTMLを生成すると次のようになります。

demo-2/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>デモページ</title>
    <meta name="description" content="これはデモページです">
  </head>
  <body>
    <h1 id="heading">見出し</h1>
    <p class="paragraph">本文です。isDemoがfalseであれば表示されます。</p>
  </body>
</html>

isDemotrueであれば、次のように出力されていました。

<p class="paragraph -demo">デモの本文です。isDemoがtrueであれば表示されます。</p>

ですが、isDemofalseになったことで、else側の部分が出力されました。

<p class="paragraph">本文です。isDemoがfalseであれば表示されます。</p>

サイト内で多くのページで利用されるテキストや、よく変更があるものはJSONファイルで定義して使い回すことができますし、データとテンプレートを分けることでメンテナンスもしやすくなります。

Pugが活用できそうなプロジェクト

ここまで、Pugの利用環境を整え、実際の例を通してその特徴を紹介しました。1回目の最後に、Pugをどんなプロジェクトで使ったらよいのか、筆者の考えを述べたいと思います。

最近の筆者は、Vue.jsやAngularを使ったプロジェクトに関わることが多くあります。そういったプロジェクトではそもそもサイトに共通する部分であったり、使いまわしたい部分がコンポーネントとして別々のファイルに分けられています。そうなってくると、Pugをはじめとするテンプレートエンジンが持つ「共通部分を使い回す」「レイアウトとコンテンツ部分を分ける」という部分に特に魅力を感じなくなってきます。

もちろん、Vue.jsであったりAngularやReactと、Pugを組み合わせることもできます。閉じタグを忘れたり入れ子がわかりづらくなることはあるので、Pugを使えばVue.jsなどでHTMLを書く際のうっかりミスを減らすことはできるでしょう。しかし、「HTMLが書きやすくなる」というだけでPugを採用するのも考えものかもしれません。Pugで始めてしまうと、あとからやっぱりやめて普通のHTMLで書き直すということは簡単にはできなくなります。また、最初は良くても後々、他のプラグインと組み合わせたらうまく動かないこともあるかもしれません。ですから、Pugを採用する時は慎重に下調べをしてから、判断をするほうが良いでしょう。

つまり、あらゆる場面において「Pugが使えて便利」というわけではないのだと筆者は考えます。すでにPug(Jade)のコードがあるプロジェクトを再利用するときであったり、Vue.jsやAngularなどを使っていないシンプルなサイトを作る場面であればPugを使えそうです。また、Eleventyのように、Pugをはじめいろいろなテンプレートエンジンに公式で対応している静的サイトジェネレータを使うのであれば、Pugを採用しても良いのかもしれません。

Pug自体は筆者好みのテンプレートエンジンです。ですが、Pugをプロジェクトに採用する際には、本当に必要かどうかを考えたほうが良いでしょう。

ここまでのまとめ

今回は、Pugの特徴や簡単な使い方を紹介しました。

なお、本記事では、Pugの機能の説明に注力するために、一番簡潔なpug-cliを使用しています。実務ではgulpやwebpackといったツールと組み合わせたり、ExpressのテンプレートエンジンとしてPugを使うこともできます。gulpとの組み合わせは、次回以降解説します。

次回はPugの記法について紹介します。