はじめに
HTMLを書いていて、</div>の閉じ忘れや、深くネストしたタグの対応関係を目で追う作業に疲れたことはありませんか。開始タグと終了タグが延々と続くマークアップは、書くのも読むのも地味に体力を削られます。
Pugは、そんな悩みを解決するテンプレートエンジンです。Hamlに影響を受けたインデントベースの記法を採用しており、閉じタグを一切書かずにHTMLを組み立てられます。Node.js製ですが、コンパイル後の関数はブラウザ上でも動作するため、サーバーサイドとクライアントサイドの両方で使える点も魅力です。
Pugの記法は独特なので、説明を読むより先に触ってみるのが理解の近道だと思います。先に挙動を見たい方はこちらからどうぞ。
Pugとは
Pugは、JavaScriptで実装されたテンプレートエンジンで、"clean, whitespace sensitive syntax for writing HTML"(空白に意味を持たせた、簡潔なHTML記法)を標榜しています。もともとJadeという名前でしたが、商標の都合でPugに改名され、現在はpugjs organizationのもとで開発が続いています。GitHubのスター数は2万を超え、Expressのデフォルトビューエンジンの1つとしても長く使われてきた実績があります。
主な特徴
- 閉じタグ不要 - インデントで階層構造を表現するため、
</div>や</p>を書く必要がありません - サーバー・ブラウザ両対応 -
pug.render()はNode.jsだけでなく、バンドルすればブラウザ上でも動作します - 組み込みのロジック機能 - 条件分岐(
if/else)、ループ(each)、変数展開、テンプレートの継承(extends/block)を素のJavaScriptに近い記法で書けます - Mixin対応 -
mixinでテンプレートの部品を関数のように定義し、再利用できます
インストール
npmでパッケージとしてインストールします。
npm install pug
コマンドラインから直接コンパイルしたい場合は、CLIツールを別途インストールします。
npm install pug-cli -g
Pugのサンプルを動かす
pug.render()にPugの文字列とデータオブジェクトを渡すだけで、HTML文字列を生成できます。以下は入力フォームの値をもとに、Pugのifとeachを使ってHTMLを組み立てる例です。名前欄を空にすると条件分岐の挙動が、タグを増やすとループの挙動が確認できます。
import pug from "pug@3.0.4"
const template = `
if name
h2 Hello, #{name}!
else
h2 Hello, stranger!
ul
each tag in tags
li= tag
`
const html = pug.render(template, {
name: "Pug",
tags: ["template", "engine", "fast"],
})
// => "<h2>Hello, Pug!</h2><ul><li>template</li>...</ul>"
実際に動かせるものが下です。入力欄に名前を入れたりタグを追加したりすると、生成されるHTMLがその場で切り替わります。
名前欄を空にするとif nameの条件が偽になり、「Hello, stranger!」に切り替わるのが確認できます。タグ欄をカンマ区切りで増やすと、eachループが回った分だけ<li>が増えます。このように、pug.render()は文字列テンプレートとデータを渡すだけで完結するため、ビルドツールなしでも試せるのがポイントです。
基本的な使い方
もっとも基本的な使い方は、Pugのテンプレート文字列をpug.render()に渡してHTMLへ変換することです。
import pug from "pug"
const html = pug.render("p Hello World!")
console.log(html) // <p>Hello World!</p>
インデントで階層を表現するため、ネストしたタグも次のように書けます。
div.container
h1 タイトル
p
| 本文の一部です。
a(href="https://example.com") リンク
Node.jsでファイルからレンダリングする場合はrenderFileを使い、Express連携ではapp.set("view engine", "pug")と設定するだけでビューエンジンとして組み込めます。
実践的なユースケース
compileClientでテンプレートを事前コンパイルする
毎回pug.render()で文字列をパースし直すのはコストがかかります。pug.compileClient()を使うと、Pugテンプレートを依存なしで単体実行できるJavaScript関数のソースコードに変換できます。ビルド時にテンプレートを事前コンパイルしておき、実行時はPug本体を読み込まずにその関数だけを配布する、という使い方が可能です。
商品名や価格を書き換えると、事前コンパイル済みの関数renderProductがその場でHTMLを再生成します。下のコード欄にはcompileClientが生成した関数のソースが表示されており、これがPugランタイムなしで動く独立した関数であることが分かります。実運用ではこの出力をビルド時にファイル出力し、evalは使わずそのままバンドルに含めます。
Mixinで繰り返し部品を再利用する
同じ構造のUI(カードやリスト項目など)を何度も書く場面では、Pugのmixinが便利です。関数のように引数を受け取れるテンプレート部品を定義し、+mixin名(引数)で呼び出せます。
mixin card(title, body)
div.card
h3= title
p= body
+card('お知らせ', '本日メンテナンスを実施します')
+card('更新情報', 'v2.0をリリースしました')
カード枚数を変更すると、eachループの中でmixinが繰り返し呼び出され、カードの数がリアルタイムに増減します。共通の見た目を持つ部品をmixin化しておくと、テンプレート内での重複記述を大幅に減らせます。
まとめ
Pugは、インデントベースの記法で閉じタグを書く手間を省きつつ、条件分岐・ループ・継承・Mixinといった機能を備えた実用的なテンプレートエンジンです。Node.js製ではありますが、pug.render()はブラウザ上でも動作するため、静的サイト生成からExpressのビューエンジンまで幅広い場面で活用できます。
HTMLの記述量を減らしたい、共通レイアウトを使い回したいと感じたら、一度Pugの記法を試してみてはいかがでしょうか。