2017/06/23追記
今更ですが、本記事のサンプルコードをあげました
soarflat-prototype/pug-template
サンプルコードは以下の点で記事内容と異なります。
- 拡張子がpug
- mixinを利用する必要がなかったため利用していない
- 現在のバージョンだと"http://#{metas.url}"のような"#{}"がサポートされていないため修正
はじめに
Pugの勉強のためにHTMLのテンプレート的なのをJadeで書きました。
Pugで書くとこうなる
もとのHTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keywords" content="">
<meta name="author" content="">
<meta property="og:title" content="タイトル">
<meta property="og:type" content="website">
<meta property="og:url" content="http://任意のURL">
<meta property="og:image" content="http://任意のURL/og_image.png">
<meta property="og:site_name" content="">
<meta property="og:description" content="" />
<meta property="fb:app_id" content="任意のID">
<title>タイトル</title>
<link rel="stylesheet" href="css/style.css">
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  ga('create', 'UA-XXXXXXXX-Y', 'example.com');
  ga('send', 'pageview');
</script>
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script> (window.jQuery || document .write('<script src="js/jquery-1.11.2.min.js"><\/script>')); </script>
<script src="js/scripts.js"></script>
</body>
</html>
これをPugで書くと以下のようになる。
doctype
html(lang="ja")
  head
    meta(charset="utf-8")
    meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1")
    meta(name="viewport", content="width=device-width, initial-scale=1")
    meta(name="description", content="")
    meta(name="keywords", content="")
    meta(name="author", content="")
    meta(property="og:title", content="タイトル")
    meta(property="og:type", content="website")
    meta(property="og:url", content="http://任意のURL")
    meta(property="og:image", content="http://任意のURL/og_image.png")
    meta(property="og:site_name", content="")
    meta(property="og:description", content="")
    meta(property="fb:app_id", content="任意のID")
    title タイトル
    link(rel='stylesheet', href="css/style.css")
    script.
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
      ga('create', 'UA-XXXXXXXX-Y', 'example.com');
      ga('send', 'pageview');
  body
    script(src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js")
    script.
      (window.jQuery || document .write('<script src="js/jquery-1.11.2.min.js"><\/script>'));
    script(src="js/scripts.js")
比較してみると、ただPug記法になっただけなので「Pugで書く必要なくない?」状態。
Pugらしい書き方になるようにする。
Pugらしい書き方で書くとこうなる
extends _layout
block var
  - metas = {}
  - metas.title = 'title'
  - metas.url = 'url'
  - metas.image = 'image'
  - metas.site_name = 'site_name'
  - metas.description = 'description'
block append meta
  +meta(metas)
  include _inc_meta_facebook
  +inc_meta_facebook(metas)
block title
  title
block body
block var
doctype html
html(lang="ja")
  head
    meta(charset='utf-8')
    meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1")
    meta(name="viewport", content="width=device-width, initial-scale=1")
    block meta
      mixin meta(metas)
        meta(name="description", content=metas.description)
        meta(name="keywords", content=metas.keywords)
        meta(name="author", content="")
    block title
    block link
      link(rel='stylesheet', href="css/style.css")
    block head_script
      script.
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
        ga('create', 'UA-XXXXXXXX-Y', 'example.com');
        ga('send', 'pageview');
  body
    block body
    block end_of_body
      script(src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js")
      script.
        (window.jQuery || document .write('<script src="js/jquery-1.11.2.min.js"><\/script>'));
      script(src="js/scripts.js")
mixin inc_meta_facebook(metas)
  meta(property="og:title", content=metas.title)
  meta(property="og:type", content="website")
  meta(property="og:url", content="http://#{metas.url}")
  meta(property="og:image", content="http://#{metas.image}/og_image.png")
  meta(property="og:site_name", content=metas.site_name)
  meta(property="og:description", content=metas.description)
  meta(property="fb:app_id", content="任意のID")
ディレクトリ構成は以下を前提とする。
.
├── index.pug
├── _layout.pug
└── _inc_meta_facebook.pug
Pugらしい書き方になった。(自信はない)
コンパイルされるHTML
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="description">
        <meta name="keywords">
        <meta name="author" content="">
        <meta property="og:title" content="title">
        <meta property="og:type" content="website">
        <meta property="og:url" content="http://url">
        <meta property="og:image" content="http://image/og_image.png">
        <meta property="og:site_name" content="site_name">
        <meta property="og:description" content="description">
        <meta property="fb:app_id" content="任意のID">
    <title></title>
    <link rel="stylesheet" href="css/style.css">
    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
      
      ga('create', 'UA-XXXXXXXX-Y', 'example.com');
      ga('send', 'pageview');
    </script>
  </head>
  <body>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <script>(window.jQuery || document .write('<script src="js/jquery-1.11.2.min.js"><\/script>'));</script>
    <script src="js/scripts.js"></script>
  </body>
</html>
mixinの箇所にインデントが発生していまうのが気になる。
今回利用したPugの機能
- 変数
- include
- extends
- block
- mixin
変数
Pugでは変数が利用できる。
今回の例では変数metasが存在する。
extends _layout
block var
  //- 変数metas
  - metas = {}
  - metas.title = 'title'
  - metas.url = 'url'
  - metas.image = 'image'
  - metas.site_name = 'site_name'
  - metas.description = 'description'
block append meta
  +meta(metas)
  include _inc_meta_facebook
  +inc_meta_facebook(metas)
block title
  title
block body
include
他のPugファイルを読み込む。
extends _layout
block var
  - metas = {}
  - metas.title = 'title'
  - metas.url = 'url'
  - metas.image = 'image'
  - metas.site_name = 'site_name'
  - metas.description = 'description'
block append meta
  +meta(metas)
  //- _inc_meta_facebook.pugを読み込む。
  include _inc_meta_facebook
  +inc_meta_facebook(metas)
block title
  title
block body
今回の例ではinclude _inc_meta_facebookと記述されているため、
_inc_meta_facebook.pugが読み込まれる。
mixin inc_meta_facebook(metas)
  meta(property="og:title", content=metas.title)
  meta(property="og:type", content="website")
  meta(property="og:url", content="http://#{metas.url}")
  meta(property="og:image", content="http://#{metas.image}/og_image.png")
  meta(property="og:site_name", content=metas.site_name)
  meta(property="og:description", content=metas.description)
  meta(property="fb:app_id", content="任意のID")
extendsとblock
他のPugファイルを継承する。
//- _layout.pugを敬承する。
extends _layout
block var
  - metas = {}
  - metas.title = 'title'
  - metas.url = 'url'
  - metas.image = 'image'
  - metas.site_name = 'site_name'
  - metas.description = 'description'
block append meta
  +meta(metas)
  include _inc_meta_facebook
  +inc_meta_facebook(metas)
block title
  title
block body
今回の例ではextends _layoutと記述されているため、
index.pugに_layout.pugが継承される。
block var
doctype html
html(lang="ja")
  head
    meta(charset='utf-8')
    meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1")
    meta(name="viewport", content="width=device-width, initial-scale=1")
    block meta
      mixin meta(metas)
        meta(name="description", content=metas.description)
        meta(name="keywords", content=metas.keywords)
        meta(name="author", content="")
    block title
    block link
      link(rel='stylesheet', href="css/style.css")
    block head_script
      script.
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
        ga('create', 'UA-XXXXXXXX-Y', 'example.com');
        ga('send', 'pageview');
  body
    block body
    block end_of_body
      script(src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js")
      script.
        (window.jQuery || document .write('<script src="js/jquery-1.11.2.min.js"><\/script>'));
      script(src="js/scripts.js")
includeのように他のPugファイルを読み込むが、includeとは異なり、
継承先ファイルと継承元ファイルの同じ名前のblockが紐づく。(関連付く?)
継承先と継承元のblockの関係
敬承先に敬承元と同じ名前のblockが存在しない場合、敬承元のblock内の記述が読み込まれるが
敬承先にも同じ名前のblockが存在する場合、block内の記述が上書きされる。
今回の例だと上書きされるのはblock var、block title、block body内の記述。
extends _layout
//- block varを上書きする。
block var
  - metas = {}
  - metas.title = 'title'
  - metas.url = 'url'
  - metas.image = 'image'
  - metas.site_name = 'site_name'
  - metas.description = 'description'
block append meta
  +meta(metas)
  include _inc_meta_facebook
  +inc_meta_facebook(metas)
//- block titleを上書きする。
block title
  title
//- block bodyを上書きする。
block body
//- 敬承先にblock varが存在するため、敬承先に読み込まれない。
block var
doctype html
html(lang="ja")
  head
    meta(charset='utf-8')
    meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1")
    meta(name="viewport", content="width=device-width, initial-scale=1")
    block meta
      mixin meta(metas)
        meta(name="description", content=metas.description)
        meta(name="keywords", content=metas.keywords)
        meta(name="author", content="")
    //- 敬承先にblock titleが存在するため、敬承先に読み込まれない。
    block title
    //- 敬承先にblock linkは存在しないため、敬承先に読み込まれる。
    block link
      link(rel='stylesheet', href="css/style.css")
    //- 敬承先にblock head_scriptは存在しないため、敬承先に読み込まれる。
    block head_script
      script.
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
        ga('create', 'UA-XXXXXXXX-Y', 'example.com');
        ga('send', 'pageview');
  body
    //- 敬承先にblock bodyが存在するため、敬承先に読み込まれない。
    block body
    //- 敬承先にblock end_of_bodyは存在しないため、敬承先に読み込まれる。
    block end_of_body
      script(src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js")
      script.
        (window.jQuery || document .write('<script src="js/jquery-1.11.2.min.js"><\/script>'));
      script(src="js/scripts.js")
敬承先に敬承元と同じ名前のblockが存在する場合、
敬承先のblock内に記述自体がなくても、敬承元のblock内の記述は読みこまれないので注意。
block append
blockと異なり、記述を上書きをするのではなく
敬承元の記述の後に敬承先の記述を追加できる。
extends _layout
block var
  - metas = {}
  - metas.title = 'title'
  - metas.url = 'url'
  - metas.image = 'image'
  - metas.site_name = 'site_name'
  - metas.description = 'description'
//- 敬承元のblock meta内の記述の後にblock append meta内の記述が追加される。
block append meta
  +meta(metas)
  include _inc_meta_facebook
  +inc_meta_facebook(metas)
block title
  title
block body
block var
doctype html
html(lang="ja")
  head
    meta(charset='utf-8')
    meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1")
    meta(name="viewport", content="width=device-width, initial-scale=1")
    //- 敬承先にblock append metaが存在するため、敬承先に読み込まれる。
    block meta
      mixin meta(metas)
        meta(name="description", content=metas.description)
        meta(name="keywords", content=metas.keywords)
        meta(name="author", content="")
    block title
    block link
      link(rel='stylesheet', href="css/style.css")
    block head_script
      script.
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
        ga('create', 'UA-XXXXXXXX-Y', 'example.com');
        ga('send', 'pageview');
  body
    block body
    block end_of_body
      script(src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js")
      script.
        (window.jQuery || document .write('<script src="js/jquery-1.11.2.min.js"><\/script>'));
      script(src="js/scripts.js")
イメージとしては以下のようになる。
mixin meta(metas)
  meta(name="description", content=metas.description)
  meta(name="keywords", content=metas.keywords)
  meta(name="author", content="")
+meta(metas)
include _inc_meta_facebook
+inc_meta_facebook(metas)
block prepend
敬承元の記述の前に敬承先の記述を追加できる。
block append metaをblock prepend metaにすると
イメージとしては以下のようになる。
+meta(metas)
include _inc_meta_facebook
+inc_meta_facebook(metas)
mixin meta(metas)
  meta(name="description", content=metas.description)
  meta(name="keywords", content=metas.keywords)
  meta(name="author", content="")
mixin
関数のような再利用できるblockを生成できる。
今回の例では_layout.pugにmixin meta(metas)が存在し、
mixin meta(metas)
  meta(name="description", content=metas.description)
  meta(name="keywords", content=metas.keywords)
  meta(name="author", content="")
_inc_meta_facebook.pugにmixin inc_meta_facebook(metas)が存在する。
mixin inc_meta_facebook(metas)
  meta(property="og:title", content=metas.title)
  meta(property="og:type", content="website")
  meta(property="og:url", content="http://#{metas.url}")
  meta(property="og:image", content="http://#{metas.image}/og_image.png")
  meta(property="og:site_name", content=metas.site_name)
  meta(property="og:description", content=metas.description)
  meta(property="fb:app_id", content="任意のID")
mixinを利用する時は、+を記述する。
今回の例ではblock append meta内で
mixin meta(metas)とmixn inc_meta_facebook(metas)内の記述が追加される。
extends _layout
block var
  - metas = {}
  - metas.title = 'title'
  - metas.url = 'url'
  - metas.image = 'image'
  - metas.site_name = 'site_name'
  - metas.description = 'description'
block append meta
  //- block var内の変数metasが引数として渡され、
  //- mixin meta(metas)内の記述が追加される。
  +meta(metas)
  include _inc_meta_facebook
  //- block var内の変数metasが引数として渡され、
  //- mixn inc_meta_facebook(metas)内の記述が追加される。
  +inc_meta_facebook(metas)
block title
  title
block body