Front End Programming
  1. ホーム
  2. ブログ一覧
  3. pug(旧:Jade)を使い倒して時短マークアップしよう!

pug(旧:Jade)を使い倒して時短マークアップしよう!

npmHTMLpugnode

私はページを量産したりチームでサイトを作る時Pugという言語を使ってWebサイトを作っています。普段webpackやgulpを使っている方なら導入のハードルも低いと思います。

npmでの導入の仕方と記述方法(とくに記述方法はディープ!に記載しています)をまとめました。今回はgulpやwebpackを使わないので、package.jsonさえ作ることができればすぐ始められます!

とても記事は長いので目次を利用して好きなところを読んでください。

この記事を書いた人

かみーゆ/フロントエンドエンジニア

セブ島在住の気ままなフリーランスエンジニア。テクニカルディレクター・エンジニア講師・ブリッジSEを経て今に至る。CMS concrete5エバンジェリスト。テックブログ以外も「磨耗しない人生の選択」や「海外生活」のライフスタイルについて発信。好きなものは肉とビール。

Read More

pugとは?

Pugとは、Haml(HTMLを抽象化したマークアップ言語)記法に影響を受けたJavaScriptで実装された高機能テンプレートエンジンです。

より短く、キレイかつ簡潔にコードを書くことができます。

The general rendering process of Pug is simple. pug.compile() will compile the Pug source code into a JavaScript function that takes a data object (called “locals”) as an argument. Call that resultant function with your data, and voilà!, it will return a string of HTML rendered with your data.
The compiled function can be re-used, and called with different sets of data.
公式サイト:pug

昔は、Jade(ヒスイ)と呼ばれていましたが、すでに商標登録されていたのでPugに名前が変更されました(Version 2以降)。

私の考えるpugでコーディングするデメリットとメリットを紹介します。

導入のメリット

  • 大量のページを手分けして量産できる
  • 短いコードでキレイかつ簡潔にコードが書ける
  • ヘッダーや共通部分とそれ以外を分けて書ける

pugではヘッダーや共通部分とそれ以外を分けて書ける

こちらは私が作っているサイトの実際のpugファイルです。footerやheaderなどを分けてデフォルトで読み込み、主要コンテンツだけ毎度変更しています。

導入のデメリット

  • 実務で使うためにはgulpwebpackのタスクを書いたりコマンドを覚える必要がある
  • インデントをミスると意図しないコンパイルが起きる
  • コードの書き方など多少の学習コストがかかる

実務で使うためにはgulpやwebpackのタスクを書いたりコマンドを覚える必要

こちらは私が実際に利用しているLaravel mixのwebpackのコードです。タスクを書いたりしないといけないのは少し面倒ですね。

導入方法

pugはnpm経由でインストール可能です。

npmをインストールしていることが前提です。インストールのコードは公式サイトでは以下が紹介されています。

npm install pug


今回は練習のため、pug-practiceというディレクトリを作ってみましょう。ディレクトリー名とproject名が被らないように注意してください。

npmのモジュールを管理できるようにpackage.jsonを作っておきます。VSコードであれば、ターミナルをcontrol + shift + @で開くことができます。

npm initでpackage.jsonを作成

package.jsonを作成し、npmモジュールを管理しながら、進めていきます。

package.jsonを作成は以下のコマンド。

npm init

もしくは

npm init -y

通常プロジェクト名など、対話式で入力し作成していくのですが-yのオプションを使うと面倒な入力を省くことができます。

npmのモジュールをインストール

次にpugをインストールします。

npm install pug pug-cli -D

場合によってインストールでコケるのでsudo(管理者権限で実行)をつけてみてください。

※ テンプレートを分けたい場合は、pug-cliを再インストールする必要があります。
テンプレート化したい場合に使う記述方法を参照してください。


-Dオプションで現在のプロジェクト管理下のみでモジュールが使えるようになります。

現時点ではグローバルにインストールせず、プロジェクト毎にモジュールをインストールするのが主流です。

package.jsonファイルが以下のようになっていたら成功です。

現時点でpugのバージョンは3.0.0でした。

今回はnpmだけで実行してみます。scriptsにpugのコマンドを追加します。

{
  "name": "pug-practice",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "pug": "pug ./src --hierarchy -o ./dist -P"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "pug": "^3.0.0",
    "pug-cli": "^1.0.0-alpha6"
  }
}
"pug-w": "pug ./src --hierarchy -o ./dist -P -w"

※scriptのオプションは記事の最後にあります。

実行は以下のコマンド。

npm run pug-w


今回用意するディレクトリーの構造です。srcディレクトリーにindex.pugを追加します。

/pug-practice
  ├ node_modules/
  ├ package.json
  ├ src/
  |  └ index.pug(追加)
  └ dist/
     └ index.html(勝手に出力されます)

src内のファイルを更新するとコンパイルされます。-wオプションが付いているのでリレンダー(再コンパイル)されます。

※scriptのオプションは記事の最後にあります。

基本の記述方法

記述方法です。多分永久保存版。

基本中の基本・単純なタグと文字列を出力したい場合

pugの記法はタグ名+スペース+コンテンツです。閉じタグは不要です。

p hello,pug!

これでファイルはコンパイルできましたね!!

<p>hello,pug!</p>


htmlを直に書くことができます。

div
  <p>hello,pug!</p>
<div><p>hello,pug!</p></div>


=(イコール)でタグ内にコンテンツを内包させることもできます。

p='こんにちは。IT戦士のかみーゆです。'
<p>こんにちは。IT戦士のかみーゆです。</p>

エスケープ処理

出力内容をエスケープしたくないことがありますよね? そんなときは!(エクスクラメーション・マーク)を使うとそのまま出力されます。

p='見出し1は<h1>タグで囲みます'

p!='見出し1は<h1>タグで囲みます'
<p>見出し1は&lt;h1&gt;で囲みます。</p>

<p>見出し1は<h1>で囲みます。</p>

コメント

コメントをpug上には残して、htmlには反映したくない場合は /(スラッシュ)2つ+-(ハイフン)を文前に追加。

//- ここにコメントをかく

htmlのコメントとして残したい場合は /(スラッシュ)2つです。

// ここにコメントをかく

入れ子(ネスト)構造を書きたい場合

入れ子にしたい場合はインデント(字下げ)を使います。 以下のような書き方をします。

header
  h1 hello, pug!

コンパイル後。

<header>
  <h1>hello, pug!</h1>
</header>


もしくは:(コロン)+スペースを使います。

div: p test

コンパイル後。

<div>
  <p>test</p>
</div>

idとclassの出力

idは#、classは.でつなぐ。

section#hoge
div.piyo

コンパイル結果。

<section id="hoge"></section>
<div class="piyo"></div>

またidやclassを使うとき、divは省略できます。

.piyo

srcやhlefなどの属性の出力方法

属性は()内に属性名=値で記載できます。複数あるときは、,(カンマ)スペースで区切ります。

img(src="image.jpg", alt="画像", width="300", height="200")

a(href="./" target="_blank") トップページ

コンパイル後

<img src="image.jpg" alt="画像" width="300" height="200"/>

<a href="./" target="_blank">トップページ</a>


id、クラスも同様にカッコ内にも書けます。

section(id="hoge")

div(class="piyo")


マルチラインで書くこともできます。

input(
  type='checkbox'
  name='agreement'
  checked
)

出力結果。

<input type="checkbox" name="agreement" checked="checked"/>


`(バッククォート)を使えば、属性の値を複数行にまたいで書くこともできる。

input(data-json=`
  {
    "very-long": "piece of ",
    "data": true
  }
`)

出力結果。

<input data-json="
  {
	  &quot;very-long&quot;: &quot;piece of &quot;,
    &quot;data&quot;: true
  }
" />


さらに&attributes()を使って、後から属性を連想配列として追加できます。

a(href="/")&attributes({'style':'color:white','target':'_blank'}) リンク

出力結果です。

<a href="/" style="color:white;" target="_blank">リンク</a>

改行追加

インライン扱いされるようなタグは改行されずコンパイル時美しく整形されません。 改行を加えたい場合は |(パイプ)2つを使います。

a(href="/") HOME
|
|
a(href="/blog") Blog

出力結果はこちら。

<a href="/">HOME</a>
<a href="/blog">Blog</a>

文字列の中にタグ

文字列の中に文字を入れる方法が2つあります。 1つは |(パイプ)を使う方法です。

p
 | これは
 strong テキスト
 | です

もう一つは#[]で囲む方法です。

p これは#[strong テキスト]です

前述したものに比べてこちらの方がはるかに簡単なのでよく使います。


出力結果はこちら。

<p>これは<strong>テキスト</strong>です</p>

JSやstyleを追加したい場合

headタグ内やbody内にJSやCSSのコードを追加したいときは以下のように書きます。

style.
  a {
    color: #333;
    background: #aaa;
  }

script.
  let pug = 'Hello, pug!!!';
  console.log(pug);

出力結果はこちら。

<style>
  a {
    color: #333;
    background: #aaa;
  }
</style>

<script>
  let pug = 'Hello, pug!!!';
  console.log(pug);
</script>

プログラミング的記述方法

分岐や変数、ループの書き方です。

ちょっと難しくなりますが、ムリに使わなくてもpugは十分書けます。

変数

pugでは変数が使えます。何気に便利。コンテンツとして出力したいときは #{} の中に記入します。

- var name = "aaa"

p #{name}

出力結果はこちら。

<p>aaa</p>


属性の中に変数を入れるときはこんな感じで書きます。

//- 変数のみ
input(type="text" value=name)

//- 変数 + 文字列
input(type="text" value=name + 'です')


文字列と混在させるときは変数を ${} 内に記入し `(バッククオート)で囲むと前述したものより可読性が上がります。

//- 変数 + 文字列
input(type="text" value=`${name}です`)

出力結果です。

<input type="text" value="aaa"/>
<input type="text" value="aaaです"/>


三項演算子で出力を分けることもできます。

- var toppage = true

h1(class=toppage ? 'is-top' : 'is-lower') 見出し

出力結果です。

<h1 class="is-top">見出し</h1>


そのまま変数を出力したい場合は \(バックスラッシュ)を使用します。

p \#{pug}は変数です。

出力結果です。

<p>#{pug}は変数です。</p>


&attributes()を使えば、属性を配列に渡して値を追加可能です。

- var attributes = {};
- attributes.target = '_blank';
a(href="/")&attributes(attributes) リンク

出力結果です。

<a href="/" target="_blank">リンク</a>

ifやcaseなどの分岐処理

if文です。ヘッダーロゴのリンクをつけるつけないなどの判定によく使います。

- var pageId = 'home'
- var pageName = 'my site'

if pageId == 'home'
  h1 #{pageName}
else
  p: a(href="/") #{pageName}

出力結果です。

<h1>my site</h1>


いわゆるswhich文のような書き方もできます。

- var  pageTemplate = 'contact'
case pageTemplate
  when 'contact'
    section.contact
  when 'top'
    section.top
  default
    section

出力結果です。

<section class="contact"></section>

breakで抜けることもできます。

default
- break

:(コロン)を使うとまとめて書けます。

- var  pageTemplate = 'contact'
case pageTemplate
  when 'contact': section.contact
  when 'top': section.top
  default: section

for、each、while文などのループ処理

ループもできます。

まずはfor文から。

ul
  - for (var i = 0; i <= 2; i++) {
    li.item リスト0#{i}
  - }

出力結果。

<ul>
  <li class="item">リスト00</li>
  <li class="item">リスト01</li>
  <li class="item">リスト02</li>
  <li class="item">リスト03</li>
</ul>


while文も使えます。出力結果は先ほどのfor文と一緒です。

- var n = 0;
ul
  while n < 3
     li.item リスト0#{n++}


each文はより実用的に使えると思います。

ul
  each val in [1, 2, 3]
    li= val

出力結果。

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>


インデックスも合わせて利用する場合。

ul
  each val, index in ['cat', 'dog', 'rabbit']
    li= index + ': ' + val

出力結果。

<ul>
  <li>0: 猫</li>
  <li>1: 犬</li>
  <li>2: うさぎ</li>
</ul>


連想配列パターン。共通の変数として宣言さえしておけばメニューなんかにめっちゃ使える。

三項演算子と組み合わせると、クラスに現在のページというクラスも追加できます。

- var menu = {'cat': '猫', 'dog': '犬', 'rabbit': 'うさぎ'}
- pageId = 'cat'
ul
  each val, key in menu
    li(class=key == pageId ? 'is-current' : '' ): a(href=`/${key}`)=val

出力結果。

<ul>
  <li><a href="/cat"></a></li>
  <li><a href="/dog"></a></li>
  <li><a href="/rabbit">うさぎ</a></li>
</ul>


さらにちょいテク。pugはそもそもJSのメソッドが使えるので、値を大文字(アッパーケース)化してそのままタイトルに使う方法もあります。

ul
  each val in ['cat', 'dog', 'rabbit']
    li: a(href=`/${val}`)=val.toUpperCase()

出力結果。

<ul>
  <li><a href="/cat">CAT</a></li>
  <li><a href="/dog">DOG</a></li>
  <li><a href="/rabbit">RABBIT</a></li>
</ul>

関数・mixin

再利用したいときはmixinに登録しておきましょう。

mixin list
  ul
    li CAT
    li DOG
    li RABBIT
+list

出力結果。

<ul>
  <li>CAT</li>
  <li>DOG</li>
  <li>RABBIT</li>
</ul>


引数を使うパターン。下層ページのタイトルやパンくずで使えそう。

mixin pageHeader(pageId, pageName)
  header(class=pageId)
	//- toUpperCase()でローマ字大文字に
    p #{pageId.toUpperCase()}
    h1 #{pageName}

- var pageId = 'service'
- var pageName = '私たちのサービス'
//- 引数のデフォルトが出力される
+ pageHeader

//- 値を変更可能
+ pageHeader(pageId, pageName)

出力結果。

<header class="service">
  <p>SERVICE</p>
  <h1>私たちのサービス</h1>
</header>


引数にはデフォルトも設定しておけます。

mixin pageHeader(pageId='top', pageName='トップページ')

出力結果です。

<header class="top">
	<p>TOP</p>
  <h1>トップページ</h1>
</header>


スプレッド構文を利用するパターン

mixin list(id, ...items)
  ul(id=id)
    each item in items
      li= item
+list('my-list', 1, 2, 3, 4)

出力結果です。

<ul id="my-list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>

テンプレート化したい場合に使う記述方法

ファイルを分けて、テンプレート化していきます。

このままではコンパイル時に、_(アンダースコア)がついたファイルもコンパイルされてしまうので、「pug-cliをGitHub – pugjs/pug-cli: Pug’s CLI interface」からインストールし直す必要があります。

npm i github:pugjs/pug-cli#master -D

参照:pug-cliで_(アンダースコア)がついたファイルやディレクトもコンパイルされてしまう問題を解決する

include・ファイルの読み込み

ファイルを読み込みたい場合は include+ペース+ファイルパスです。

拡張子(.pug)は省略できます。

include _inc/_header

extend・継承

extendとblockを使って元となるファイルを継承することもできます。 たとえば、_layout.pugというテンプレートを使いたい場合は次のような書き方になります。

extend _layout

append・独自の設定

ページごとに独自の設定をもたせたいときはappendを使います。

//- _layout.pug
block slider
//- home.pug
append slider
  script(src="slider.js")

テンプレートサンプル(簡易バージョン)

ディレクトリー構成の例です。超簡易バージョンなので、実務用にするためには少し手を加える必要があります。

/pug-practice
  ├ node_modules/
  ├ package.json
  ├ src/
  |  ├ _inc/(追加)
  |  |  ├ _layout.pug(追加)
  |  |  ├ _header.pug(追加)
  |  |  └ _footer.pug(追加)
  |  └ index.pug
  └ dist/
     └ index.html(勝手に出力されます)

index.pugをテンプレート出力するためには以下のように書くと良いと思います。

layout.pug・レイアウト用テンプレート

//- Setting
block variables
append siteInfo
  - var siteName = 'My Site'
  - var menu     = {'home': 'トップページ', 'service': 'サービス', 'about': '私たちについて', 'contact':'お問い合わせ'}
  //- メニューの出力
  mixin list(current='', list=menu)
      ul
        each val, key in list
          - var key = key === 'home' ? '' : key
          if current !== ''
            li(class=val === current ? 'is-current' : '' ): a(href=`/${key}`)=val
          else
            li: a(href=`/${key}`)=val

//- コンテンツ
<!DOCTYPE html>
html
  head
    title=pageName
    meta(name="description" content=pageDescription)
  body
    include _header.pug
    main
      block content
    include _footer.pug

header.pug・ヘッダー用テンプレのコード

//- setting
block siteInfo
block variables

//- コンテンツ
header
  if pageId === 'index'
    h1 #{siteName}
  else
    p: a(href="/") #{siteName}

nav
  +list(pageName)

footer.pug・フッター用テンプレのコード

//- setting
block siteInfo

//- コンテンツ
footer
  +list
  p: small (c) #{siteName}

index.pug・コンテンツ用テンプレのコード

//- setting
extend _layout

append variables
  - var pageId = 'index' //- Name of the page
  - var pageName = 'トップページ'
  - var pageDescription = 'ページの説明'

//- コンテンツ
append content
  h1 #{pageName}
  p test

出力結果

<!DOCTYPE html>
<html>
  <head>
    <title>トップページ</title>
    <meta name="description" content="ページの説明"/>
  </head>
  <body>
    <header>
      <h1>My Site</h1>
    </header>
    <nav>
      <ul>
        <li class="is-current"><a href="/">トップページ</a></li>
        <li><a href="/service">サービス</a></li>
        <li><a href="/about">私たちについて</a></li>
        <li><a href="/contact">お問い合わせ</a></li>
      </ul>
    </nav>
    <main>
      <h1>トップページ</h1>
      <p>test</p>
    </main>
    <footer>
      <ul>
        <li><a href="/">トップページ</a></li>
        <li><a href="/service">サービス</a></li>
        <li><a href="/about">私たちについて</a></li>
        <li><a href="/contact">お問い合わせ</a></li>
      </ul>
      <p><small>(c) My Site</small></p>
    </footer>
  </body>
</html>

まとめ

いかがでしたか?

npmでcliで読み込むと、webpackやgulpどちらにでもカンタンに組み込めるので便利かと思います!

今回はよく忘れる変数やループ処理などの使い方をかなりディープにまとめました。

pugの公式サイトを結構頑張って網羅してる(つもり.. )のでよかったら参考にしていただけると嬉しいです。

次回はもう少し実務で使えるテンプレのレシピをまとめようと思います。

最後までお読みいただきありがとうございました。

おまけ・pug-cli の npmスクリプトのオプション

pug-cliのscriptのオプションです。途中まで訳しましたが、ちょっと自信なし。。。参考までに!

オプション名 詳細
-h, —help ヘルプオプションの出力
-V, —version バージョンを調べる
-O, —obj <str|path> JSON/JavaScriptのオプションオブジェクトやファイル
-o, —out <dir> HTMLやJSを<dir>(指定した名前のディレクトリ)に出力
-p, —path <path> filename used to resolve includes
-P, —pretty インデントなど整えて出力
-c, —client クライアントようにコンパイル?(compile function for client-side runtime.js)
-n, —name <str> コンパイルされたテンプレートの名前(-cオプションが必要)
-D, —no-debug デバッグなしでコンパイルする
-w, —watch ファイルを監視し、更新されたらリロードする
-E, —extension <ext> 出力ファイルの拡張子を指定
-s, —silent ログを出力しない
—name-after-file name the template after the last section of the file path (requires —client and overriden by —name)
—doctype <str> specify the doctype on the command line (useful if it is not specified by the template)
—hierarchy 階層構造を維持したまま出力

関連記事もあわせてお読みください

Web制作に関する人気の記事

セブ島・海外移住に関する人気の記事

キャリアアップ・転職に関する人気の記事