Gulp 4でテンプレートエンジンのPug(Jade)を使う
category:web
Gulpのタスクにテンプレートエンジンのpugを使えるようにします。
Pug
今更ですが、PugはもとはJadeという名称でHTMLのJavaScript Templatesの1つとされる。
Node.jsのフレームワーク Express ではhtmlビューはデフォルトでサポートされているらしい。
Ruby on Railsでのhaml/slimのような立ち位置で、ここのサイトも静的サイトジェネレーターであるMiddlemanを使ってhtmlビューはhamlによって書かれている。
Pug導入のメリットは、、、
- 要素や属性の記述が簡略化できる
- インデントに沿って記述して、閉じタグの忘れなどもなくなる
- 通常のHTMLではできないモジュールの継承や共通化が使える
- 変数が使える
- mixin(関数のように使いまわせる block
を生成)
- JavaScriptのようなオブジェクトを作ってhtml側でデータを扱える
みたいなところだと思います。
今回はビルドツールのGulpのタスクにpugコンパイルの処理を加えるのと、pugの使い方も備忘録として残したいと思います。
基本的な記述はこちらなどを参考にしてください。
必要なモジュールのインストールと設定
Gulp 4が既に使えることが前提で進めます Gulp 4サクッと導入(nodenv版)
gulp-pugをインストールします、pugコンパイルに必要なモジュールはこれだけ。
npm i -D gulp-pug
gulpfile.js設定
const { src, dest, watch, series } = require('gulp') const browser = require('browser-sync').create() const pug = require('gulp-pug') const compilePug = () => src([ 'src/pug/**/*.pug', '!src/pug/**/_*.pug' ]) .pipe( pug({ pretty: false }) ) .pipe(dest('./dist')); const watchFiles = () => { watch(['src/pug/**/*.pug'], series(compilePug,reload)); } const server = (cb) => { browser.init({ server: { https: true }, baseDir: './dist', server: "./dist", index: "index.html" }); compilePug() cb(); } const reload = (cb) => { browser.reload(); cb(); } exports.default = server;
ディレクトリ構成はこのようになります。
├ /dist │ ├ /hoge │ │ └ index.html │ └ index.html └ /src └ /pug ├ /data │ └ _data.pug ├ /hoge │ └ index.pug ├ /layouts │ └ _layout.pug ├ /mixin │ └ _mixin_.pug ├ /modules │ ├ _footer.pug │ ├ _head.pug │ └ _header.pug └ index.pug
これでpugファイルがwatchされ、自動でコンパイルされる状態になりました。
npx run gulp
それぞれのpugファイルの記述を見ていきます。
pugファイルの構造
上記のディレクトリ構造は例の1つですが、pugファイル内部でincludeされているファイルは _
の接頭辞がついて、接頭辞がつかないpugファイルがディレクト構造をそのままにhtmlファイルにコンパイルされます。
layouts/_layout.pug
まずは各htmlページの基本的な構造を定義している layouts/_layout.pug
を見ていきます。
block value doctype html html(lang='ja') //- ヘッド include ./modules/_head.pug body(class=pageClass) //- ヘッダー include ./modules/_header.pug //- ページコンテンツ block content //- フッター include ./modules/_footer.pug
htmlページのheadタグとheader要素とそのページのコンテンツ、footer要素が大まかに設定されています。
body要素には変数が入る想定でpage単位でclassがつくように指定してますが、変数のextendするpege側で定義されます。
body(class=pageClass)
modules/_head.pug
head要素です。
共通の要素はベタ書きの文字列で、pageによって変えたいコンテンツは変数を使います。
blockごと差し込みたい場合も適当に名を付けてあげます。
head meta(charset='UTF-8') meta(http-equiv='content-language', content='ja') meta(name='viewport', content='width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no') title #{title} meta(name='description', content=description) meta(name='keywords', content=keywords) block addCss block addJs
modules/_header.pug & modules/_footer.pug
相対パスで外部ファイルを読み込む場合、それをかくページで共有するときなどは相対パスの階層が変化してくるので、ページ内でpathという変数を定義してあげると、それぞれのページで正しいディレクトリ指定ができるようになります。
そしてES6のようなテンプレートリテラルも使えるので、わざわざ連結してあげる必要もないです。
Date.now()のようなJavaScriptメソッドも使えるので、たとえばよく修正のかかるファイルの読み込みにはタムスタンプをパラメータに付けてあげて、コンパイル毎にキャッシュ対策もできる。
//- ヘッダー header .headerContainer h1 a(hred="#") img(src=`${path}assets/images/sample.png?ver=${dateNow}`, alt="sample") `${path}assets/js/main.js?ver=` + dateNow //- フッター footer .footerContainer p フッター
data/_data.pug
pugテンプレートの中で配列データを持つことができます。
- var manufacturing1ListData = [{ index: '0', img: 'sample0.png', ttl: 'タイトル', desc: '説明' }, { index: '1', img: 'sample1.png', ttl: 'タイトル', desc: '説明' }, { index: '2', img: 'sample2.png', ttl: 'タイトル', desc: '説明' }, { index: '3', img: 'sample3.png', ttl: 'タイトル', desc: '説明' }, { index: '4', img: 'sample4.png', ttl: 'タイトル', desc: '説明' }, { index: '5', img: 'sample5.png', ttl: 'タイトル', desc: '説明' }];
mixin/_mixin.pug
eachを使ったmixiも作成できます。
引数で _data.pug
でオブジェクトと、そのページで定義している変数も渡しています。
mixin dataList(arr, path) each value, index in arr li figure img(src=`${path}assets/images/${value.img}`) .contents h3 !{value.ttl} p #{value.desc}
index.pug
topページに当たるpugファイルです。
このファイルのディレクトリ構成がそのままurlとなります。
extend layouts/_layout append value - var title= 'タイトル'; - var description= 'トップページディスクリプション'; - var keywords = 'キーワード1,キーワード2,キーワード3'; - var path = ''; - var dateNow = Date.now(); - var pageClass = 'index' append addCss link(rel="stylesheet" href=`${path}assets/css/style.css?ver=${dateNow}`) append addJs script(src=`${path}assets/js/main.js?ver=${dateNow}`) block content main section h2 | ページコンテンツ p | テキストテキスト br | テキストテキストテキストテキスト include data/_data.pug include mixin/_mixin.pug ul +dataList(data1, path)
上からextend layouts/_layout
をまず読み込み。
append value
でincludeで使われる変数の定義をしてます。
文字列だけの変数定義は、仮に記載するのを忘れてしまっても nullとして描画されerrorで警告が出ないので、見落とさないようにしてあげます。
block content
が layouts/_layout.pug
で読み込まれている位置に入ることになります。
継承や共有パーツのincludeの仕方はいくつかありますが、このようにpugは柔軟に対応してくれます。
include data/_data.pug include mixin/_mixin.pug ul +dataList(data1, path)
layoutからのmodulesの読み込みと同じように、dataとmixinのpugファイルを読み込んでおくと、その変数名と関数名がこのtopページの領域で呼び起こすことができます。
コンパイルされるhtmlソース
以上のpugファイル構成をコンパイルしてみると、以下のようになります。
<!DOCTYPE html>ページコンテンツ
テキストテキスト
テキストテキストテキストテキスト
タイトル
説明
タイトル
説明
タイトル
説明
タイトル
説明
タイトル
説明
タイトル
説明
1ページだけではありますが、デザインシステムを取り入れたstyleやコンポーネントと同じようにhtmlを構造化することができることがわかると思います。
最後に
静的htmlで表現しなければならない場面はたくさんあるかと思います。
コンパクトで軽量、気軽に導入できるのが何よりのメリットかと思います。