軽量なマークアップ言語 Jade 入門 からの Gulp でコンパイルまで - Gulp で作る Web フロントエンド開発環境 #5

これまでずっと Rails での開発をしていたので軽量マークアップ言語に Slim を使っていましたが、Gulp 環境になっても同じような言語でマークアップしたいので Jade を導入してみたいと思います。

サンプルコードはこちらからどうぞ。

Jade とは

Jade is a high performance template engine heavily influenced by Haml and implemented with JavaScript for node and browsers. For bug reports, feature requests and questions, open an issue. For discussion join the chat room.

JavaScript Temlates (JST) の一種です。JST というと Underscore.js, Handlebars.js などが比較的有名ですが、Jade は Ruby on Rails などでよく使われる Haml や Slim と非常に似ていることから、HTML を書くための軽量マークアップ言語としても使うことが出来ます。HTML のようなタグベースのマークアップ言語と比較して記述量が大幅に少ないので、コード量が肥大化しても見通しが良いのが特徴です。

どれくらい記述量が少ないかというと、

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade</title>
    <script type="text/javascript">
      if (foo) {
         bar(1 + 5)
      }
    </script>
  </head>
  <body>
    <h1>Jade - node template engine</h1>
    <div id="container" class="col">
      <p>You are amazing</p>
      <p>
        Jade is a terse and simple
        templating language with a
        strong focus on performance
        and powerful features.
      </p>
    </div>
  </body>
</html>

このような HTML コードが、

doctype html
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript').
      if (foo) {
         bar(1 + 5)
      }
  body
    h1 Jade - node template engine
    #container.col
      if youAreUsingJade
        p You are amazing
      else
        p Get on it!
      p.
        Jade is a terse and simple
        templating language with a
        strong focus on performance
        and powerful features.

これくらいスッキリとした見た目になります。そんな訳で、Gulp 上で Jade を動かしてみたいと思います。

Jade を Gulp 上でコンパイルしてみる

まずは Jade コンパイラをインストールします。


続けて Gulp 上で動かすために gulp-jade もインストールします。


適当に Jade ファイルを作成します。最初なので単一ファイルのシンプルなものにします。

doctype html
html
  head
    meta(charset='UTF-8')
    title Hello, Jade!
  body
    h1 Hello, Jade!
    p これは Jade のチュートリアルです。

タスクを定義します。

var gulp = require('gulp');
var jade = require('gulp-jade');
gulp.task('jade', () => {
    return gulp.src(['./src/jade/**/*.jade', '!./src/jade/**/_*.jade'])
        .pipe(jade({
            pretty: true
        }))
        .pipe(gulp.dest('./dest/'));
});

pretty オプションに true を指定しました。Jade はデフォルトで改行やインデントを排除した形式でコンパイルするので、pretty を true にすることで整形された見やすいコードでコンパイルすることができます。

実行してみます。

$ gulp jade
[18:36:56] Requiring external module coffee-script/register
[18:36:57] Using gulpfile ~/Documents/sandbox/try_gulp/try_jade/gulpfile.js
[18:36:57] Starting "jade"...
[18:36:57] Finished "jade" after 138 ms

生成された HTML がこちら。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello, Jade!</title>
  </head>
  <body>
    <h1>Hello, Jade!</h1>
    <p>これは Jade のチュートリアルです。</p>
  </body>
</html>

Jade の記法を一気に学ぶ

Haml や Slim に慣れすぎていると若干戸惑うところがいくらかあるので、ここで改めて基本的な記法を抑えておくとします。

環境など構築せずにちょっと動作を確認したい場合には、こちらの Playground がオススメです。

http://www.learnjade.com/playground/

要素のテキストはタグ名のうしろに半角スペース空けて記述する

h1 Hello, Jade!
div hogehoge
p Lorem ipsum
<h1>Hello, Jade!</h1>
<div>hogehoe</div>
<p>Lorem ipsum</p>

イメージとして、要素を宣言してその引数として入れ子にしたいテキストを渡すといった感じです。入れ子というキーワードが出て来ましたが、要素を入れ子構造にするやり方は次に紹介します。

要素の入れ子は改行してインデントを付ける

header
    h1 Hello, Jade!
div
    p Lorem ipsum
    img
<header>
    <h1>Hello, jade!</h1>
</header>
<div>
    <p>Lorem ipsum</p>
    <img />
</div>

明示的な閉じタグが存在しない Jade において改行とインデントは非常に重要な意味を持っています。親要素から改行してインデントを一つ付けられた要素は、入れ子された状態で HTML に書き出されます。

class と id 属性は CSS セレクタと同じ記法で書く

h1#pageTitle Hello, Jade!
div.container hogehoge
p#message.text-info.lead Lorem ipsum
.container foo
<h1 id="pageTitle">Hello, Jade!</h1>
<div class="container">hogehoe</div>
<p id="message" class="text-info lead">Lorem ipsum</p>
<div class="container">foo</div>

#記号の後ろに id 名、.記号の後ろに class 名を書きます。class は上記のように書くことで複数同時に指定することができます。要素名を省略して id や class だけを指定すると、div 要素とみなされて書き出されます。

それ以外の属性は()で囲い、カンマ区切りで書く

srchrefnametarget といった属性を指定する方法です。

a(href="google.com") Google
a(class="button", href="google.com") Google
<a href="google.com">Google</a>
<a href="google.com" class="button">Google</a>

また、JavaScript コードの表現を記述して値を動的にセットすることも出来ます。以下の例は三項演算子を使ったものです。

- var authenticated = true
body(class=authenticated ? "authed" : "anon")

属性の数が多い時は、以下のように改行して記述することが出来ます。

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

Boolean タイプの属性値の指定方法

割りと迷いがちなチェックボックスやラジオボタンの checked 属性の指定方法は以下のとおりです。

input(type="checkbox", checked)
input(type="checkbox", checked=true)
input(type="checkbox", checked=false)
input(type="checkbox", checked=true.toString())
<input type="checkbox" checked="checked"/>
<input type="checkbox" checked="checked"/>
<input type="checkbox"/>
<input type="checkbox" checked="true"/>

API の返り値に応じて動的に状態を変更する時などによく出てくるかと思います。

Style 属性の指定方法

style 属性は複数指定することをこうりょしてハッシュ型で指定します。

a(style={color: "red", background: "green"})

Class 属性の指定方法

上に書いた CSS セレクタ形式だけでなく、他の属性値と同じように指定できますが、class の場合は配列型で指定します

- var classes = ["foo", "bar", "baz"]
a(class=classes)
//- class 属性は複数に分けて記述することも出来ます。
a.bing(class=classes class=["bing"])
<a class="foo bar baz"></a>
<a class="bing foo bar baz bing"></a>

複数に分けて指定してもこのように一つの配列に push されたのと同じように処理されます。API やスクリプトに応じて動的に変化する class を別に定義したい時に使うことでしょう。

データ属性の指定方法

特別なことは何もなく、ほかの属性と同じように指定できます。

#foo(data-bar="foo", data-hoge-fuga="moge")

JavaScript, Style コードの書き方

Jade 内に JavaScript コード、Style コードを記述する際は以下のようにします。

script.
 (function() {
   console.log("hello, Jade!");
 })();
style.
 .foo {
   font-weight: bold;
   color: red;
 }
<script>
   (function() {
     console.log('hello, Jade!');
   })();
 </script>
 <style>
   .foo {
     font-weight: bold;
     color: red;
   }
 </style>

script.と末尾に.を付けるのを忘れずに。

コメントアウト

//
    この部分は
    ブロックコメントです
// 一行コメント
p Lorem ipsum
<!-- 
    この部分は
    ブロックコメントです
  -->
<!-- 一行コメント -->
<p>Lorem ipsum</p>

コメントアウト箇所も HTML 要素と同様に入れ子とインデントを意識する必要があります。

上記の例は HTML にも出力される記法ですが、以下の記法は Jade コードでのみ有効となるタイプです。

//-
    この部分は
    ブロックコメントです
//- 一行コメント
p Lorem ipsum

HTML にはコメントアウト部分が書き出されません。

JavaScriptプログラミングを取り入れる - 変数、条件式、ループ処理など

Jade は Node.js 上でコンパイルされる手前、JavaScript のプログラミング手法を使うことができます。

変数を使う

変数の定義は一行目にあるように先頭に-をつけ、その後ろに変数名を記述して値を代入します。変数の内容を出力するにはいくつか方法があり、主に以下の様な記法にて出力します。

- name = "wakamsha";
p= name
p こんにちは、#{name} さん
p
 |さようなら、#{name} さん
<p>wakamsha</p>
<p>さようなら、wakamsha さん</p>
<p>こんにちは、wakamsha さん</p>

HTML タグと同じ行内で出力するには、タグ名の直後に = をつけた後に変数名を記述します。通常の文字列と合わせて表示させるには、変数を #{} で囲みます。

例えばユーザー名など画面のいろんな箇所に同じ文言が表示されるといった場合、それぞれの箇所にベタ書きしてしまうと文言変更が発生した際に全箇所修正しなくてはなりません。変数で定義しておけば修正はその一箇所だけで済むので、ケアレスミスを犯すことなく瞬時に修正することができます。

条件式

JavaScript 構文と同様に if else が使用出来ます。

- var user = { description: "foo bar baz"; }
- var authorised = false
#user
  if user.description
    h2 Description
    p.description= user.description
  else if authorised
    h2 Description
    p.description.
      User has no description,
      why not add one...
  else
    h1 Description
    p.description User has no description
<div id="user">
  <h2>Description</h2>
  <p class="description">foo bar baz</p>
</div>

また、JavaScript にはない unless を使うことも出来ます。

ループ処理

もちろん for 文を使うことも出来ますが、Jade ではよりモダンな each 構文を使うことが出来ます。

ul
  each val in [1, 2, 3, 4, 5]
    li= val
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>

index も使うことが出来ます。

ul
  each val, index in ["zero", "one", "two";]
    li= index + ": " + val
<ul>
  <li>0: zero</li>
  <li>1: one</li>
  <li>2: two</li>
</ul>

while 構文も使えます。

- var n = 0
ul
  while n < 4
    li= n++
&#91;/code&#93;
&#91;code lang="html"&#93;
<ul>
  <li>0</li>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>

Extends - テンプレートの継承

extends という機能を使うことで、Jade テンプレートを継承することが出来ます。もう少しわかりやすく言うと、Rails でいうところの 'Layouts' 機能と同じような事が実現できます。

doctype html
html
  head
    block title
      title Default title
  body
    block heading
    ul
      li item prompt
      block listitems

シンプルなレイアウトのサンプルを作ってみました。block title という記述がありますが、このブロックは継承先にある block title というブロックで差し替えられる(継承される)事になります。

上記テンプレート(レイアウト)を継承するテンプレートを作成します。

extends ./layout.jade
block title
  title Article Title
block heading
  h1 My Article
block append listitems
  li list-item_1
  li list-item_2
  li list-item_3

先頭に extends キーワードに継承するテンプレートのパスを指定します。そしてこのindex.jade にも _layout.jade にあるのと同じ block があり、同じ block 名同士で相互に対応しているということを表しています。

block の継承は、通常だと継承する要素で全て置換されますが、append というオプションをつけることで継承元の block を残しつつ、そこに追加することが出来ます。おなじく prepend オプションを指定すれば、継承元の block 要素の前に追加することが出来ます。

生成された HTML がこちら。

<!DOCTYPE html>
<html>
  <head>
    <title>Article Title</title>
  </head>
  <body>
    <h1>My Article</h1>
    <ul>
      <li>item prompt</li>
      <li>list-item_1</li>
      <li>list-item_2</li>
      <li>list-item_3</li>
    </ul>
  </body>
</html>

Rails などに見られる yield がブロック単位で各所に細かく設定できると考えておけばよいでしょう。かなり便利な機能です。

include について

アプリケーションの規模が大きくなってくると、適度にコンポーネント化して分割しておくことでメンテナンスコストを下げることが出来ます。Jade では include という機能がデフォルトで備わっており、指定したコンポーネントを読み込む事ができます。

doctype html
html
  include src/jade/includes/head.jade
  body
    h1 My Site
    p Welcome to my super lame site.
    include ./includes/foot.jade

読み込まれるコンポーネントを作成します。

head
  title My Site
  script(src='/javascripts/jquery.js')
  script(src='/javascripts/app.js')
#footer
  p Copyright (c) foobar

生成された HTML がこちら。

<!doctype html>
<html>
  <head>
    <title>My Site</title>
    <script src='/javascripts/jquery.js'></script>
    <script src='/javascripts/app.js'></script>
  </head>
  <body>
    <h1>My Site</h1>
    <p>Welcome to my super lame site.</p>
    <div id="footer">
      <p>Copyright (c) foobar</p>
    </div>
  </body>
</html>

Markdown ファイルも読み込むことが出来る

例えば利用規約やプライバシーポリシーなどといった静的なドキュメントに関しては、Markdown で管理しておくと良いかもしれません。

doctype html
html
  head
    title An Article
  body
    include:markdown article.md
# article.md
This is an article written in markdown.
* list item 1
* list item 2
* list item 3

生成されたHTMLがこちら。

<!doctype html>
<html>
  <head>
    <title>An Article</title>
  </head>
  <body>
    <h1>article.md</h1>
    <p>This is an article written in markdown.</p>
    <ul>
      <li>list item 1</li>
      <li>list item 2</li>
      <li>list item 3</li>
    </ul>
  </body>
</html>

Mixins について

Mixins はその名の通り block を再利用出来るようにすることができる関数のような機能で、引数を渡すことが出来るのが特徴です。Jade の include には Rails の partial のように引数といったパラメータを渡せる仕様がないため、この Mixins を活用することになります。

doctype html
html
  head
    title An Article
  body
    include _article.jade
    +article('Hello, Jade!')
    hr/
    +article('Hello, Gulp!!')
      p This is my
      p Gulp tutorial.
mixin article(title)
  .article
    .article-wrapper
      h1= title
      if block
        block
      else
        p No content provided

生成された HTML がこちら。

<!doctype html>
<html>
  <head>
    <title>An Article</title>
  </head>
  <body>
    <div class="article">
      <div class="article-wrapper">
        <h1>Hello Jade!</h1>
        <p>No content provided</p>
      </div>
      <hr />
      <div class="article-wrapper">
        <h1>Hello Gulp!!</h1>
        <p>This is my</p>
        <p>Gulp tutorial.</p>
      </div>
    </div>
  </body>
</html>

締め

もともと Haml から始まって Slim に慣れ親しんでいたので、Jade もすんなりと修得することが出来ました。普段このような書式のマークアップ言語に馴染みがない方にとっては違和感が強いかもしれません。自分もはじめは戸惑いました。が、コード量が圧倒的に少なく閉じタグをミスる心配がなくなることにメリットを感じて修得してからは格段に生産性が上がったと思います。

一部で JavaScript は Web ブラウザのアセンブラ言語になりつつあるなんて言われていますが、HTML も同じように言われる日が来るかもしれませんね。