PUGを使った静的サイト制作の効率アップの方法を紹介します。
Pugを使う目的
Pugは主にHTMLページの制作をスピードアップ・効率化するためのツールです。EJSと使用目的とできることは同じですので、制作会社からの要求に合わせてどちらを使うか決めるのが良いと思います。
- PugによるHTMLページのパーツ分割
- Pugで変数を使う方法
- Pugで配列とループを使う方法
- Pugでパスを変数化する方法
一般的にはWordPressで使うことはあまりないようです。
Pugの環境構築
Pugはgulpで使うことを前提したツールです。
Pugのインストール
Pugのインストール手順をザックリまとめると下のとおりです。
- Visual Studio Code(以下、VSCode)でサイト制作用の作業フォルダを開きます。
- VSCodeの新規ターミナルで下記コマンドを入力・リターン
npm init -y - VSCodeの新規ターミナルで下記コマンドを入力・リターン
npm install —save-dev gulp gulp-pug - gulpfile.jsを新規作成、内容を編集する
一つずつ解説していきます。
VSCodeで作業フォルダを開く
サイト制作の作業フォルダの構成は人それぞれですが、今回は一例ということでご理解ください。
DEMOサイト(フォルダ)
CSS(フォルダ)
style.css
image(フォルダ)
company(フォルダ)
index.html
contact(フォルダ)
index.html
index.html
gulpとgulp-ejsのインストール
VSCodeメニューバーからでターミナル>新規ターミナルを開き、下記コマンドをVSCodeのターミナルに入力し、リターンキーを押す。
npm init -y
続いて、下記コマンドをVSCodeのターミナルに入力し、リターンキーを押す。
npm install --save-dev gulp gulp-pug
インストールが成功すると下記2つのファイルと1つのフォルダが生成される。
- package.json
- package-lock.json
- node_modulesフォルダ
そして、package.jsonを開くと、下記のようにgulpとgulp-pugがインストールされていることが確認できる。
gulpfile.jsの新規作成と編集
作業フォルダの一番上の階層にgulpfile.jsを新規作成し、下記内容を記述する。
const gulp = require("gulp");
const pug = require("gulp-pug");
function compilePug() {
// コンパイル前のファイルのパス
return gulp.src("./src/**/*.pug", "!./src/**/_*.pug")
// コンパイルの処理
.pipe(pug())
// コンパイル後のファイルのパス
.pipe(gulp.dest("./public"))
}
exports.compilePug = compilePug;
パスに「“!./src/*/_.pug”」と書くと、そのパスで指定されたファイルはコンパイルされない。上の場合は、_で始まるファイルの出力を禁止している。
フォルダとファイル構成を編集
- 新たに、srcフォルダとpublicフォルダを作る
- srcフォルダに編集時に使うファイルをすべて入れ、すべてのHTMLファイルに対して拡張子.pugのファイルを新規作成する
- publicフォルダは基本そのままだが、今回は最低限htmlの生成ができればよいのでCSSとimageフォルダをassetsフォルダに入れた状態でpublicフォルダに移動
- パスが正しく動作するように確認と編集
DEMOサイト(フォルダ)
public(フォルダ)
assets(フォルダ)
CSS(フォルダ)
style.css
image(フォルダ)
src(フォルダ)
company(フォルダ)
index.html
index.pug
contact(フォルダ)
index.html
index.pug
index.html
index.pug
node_modules
gulpfile.js
package-lock.json
package.json
この状態で、ターミナルに下記コマンドを入力しリターンキーを押すと、publicフォルダ内にhtmlファイルが生成されPugの動作確認ができる
npx gulp compilePug
Pugファイルを自動改行するオプションの設定
Pugファイル上で改行されていても、そのままではコンパイル後のhtmlファイルでは未改行の状態で出力されてしまう。この対策として、gulpfile.js内に改行オプションを記述しておく。
Pugの改行オプションの抜粋
.pipe(pug({
pretty: true
}))
Pugの改行オプションを加えたgulpfile.jsの全文
const gulp = require("gulp");
const pug = require("gulp-pug");
function compilePug() {
// コンパイル前のファイルのパス
return gulp.src("./src/**/*.pug", "!./src/**/_*.pug")
// コンパイルの処理を書く
.pipe(pug({
pretty: true
}))
// コンパイル後のファイルのパス
.pipe(gulp.dest("./public"))
}
exports.compilePug = compilePug;
Pugの書き方(超基本)
Pugを利用するためには、コンパイル元のPugファイルにPug記法に変換したhtmlを書く必要がある。
Pugの基本記法
- タグ:<>を省略
- 属性:()で囲う
例文)
<html lang=”ja”> → html(lang=”ja”)
Pugの階層構造と閉じタグ
階層構造と閉じタグはtabのインデントで制御している(下の例文を参照)
例文:HTML
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>DEMOタイトル</title>
<meta name="description" content="DEMOのディスクリプション">
</head>
例文:Pug
html(lang="ja")
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
title DEMOタイトル
meta(name="description" content="トップページのディスクリプション")
Pugのクラス名の書き方
class属性は、タグ名の後ろに.クラス名で連続して書く
<header class=”header”> → header.header
複数のクラスは.で連続して書く
<div class=”header__inner inner”> → div.header__inner.inner
なお、divタグは省略出来る
<div class=”header__inner inner”> → .header__inner.inner
Pugのコメントアウトの書き方
コメントはJavaScriptと同じように//の後ろにコメントを書けば良い
<!– Font–> → // Font
Pugで改行せずに1行にまとめて書く方法
h1.header__logo
a(href=”./index.html”) Logo
上の文を1行にまとめる場合には: (コロン+スペース)で繋げると可能
h1.header__logo: a(href=”./index.html”) Logo
Pugの変数
拡張子.pugファイル内での変数の書き方を紹介します。
Pugの変数宣言
const 変数名 = “値”;
例文は下記のとおり
- const menuCompany = "会社情報";
- const menuContact = "お問い合わせ";
Pugの変数の出力
書き方は2種類あり、どちらでも良い。
記法1|Pugの変数
#{変数名}
使用例は下記の通り
a.header__link(href="./contact/index.html") #{menuCompany}</a>
#{変数名}の前にはスペースを空けること
記法2|Pugの変数
`{$変数名}`
使用例は下記のとおり
a.header__link(href="./contact/index.html") `{$menuContact}`
Pugの配列(ループ)
Pugの配列の書き方
配列の定義
-
const 配列名 = [
{
キー1: "値1",
キー2: "値2",
},
{
キー1: "値3",
キー2: "値4",
},
];
以下、例文。
-
const muenus = [
{
name: "会社情報",
url: "./company/index.html"
},
{
name: "お問い合わせ",
url: "./contact/index.html"
},
];
配列の出力(ループ)
each 変数 in 配列名
a.header__link(href=変数.キー1) #{変数.キー2}
例文は以下のとおり。
nav.header__nav
each menu in menus
a.header__link(href=menu.url) #{menu.name}
#{変数名}の前にはスペースを空けること
この状態で下記コマンドを入力リターンすると、pugからhtmlへコンパイルされる
npx gulp compilePug
Pugのパーツ分割
Pugによるパーツファイルに分割する方法を紹介します。
基本構文|Pugのパーツ分割
Pugファイル分割の基本操作は下記のとおりです。
- Pugファイルのパーツ化したい部分を別のPugファイルにカット&ペースト
- 元Pugファイルのカットして空いた場所に下記構文を入れる
include 分割ファイルへのパス
例文:footerをパーツ化する場合
- _footer.pugを新規作成する(通常はsrcフォルダ内にpartsフォルダを新設し、その中に新規作成する)。
DEMOサイト(フォルダ)
public(フォルダ)
assets(フォルダ)
CSS(フォルダ)
image(フォルダ)
src(フォルダ)
company(フォルダ)
contact(フォルダ)
parts(フォルダ)
_footer.pug
index.html
index.pug
node_modules
gulpfile.js
package-lock.json
package.json
2. footer.pugの内容:元のindex.pugからフッター部分をそのままカット&ペースト(インデントの調整必要)
footer.footer
.footer__inner.inner
.footer__copyright Copyright © 2023 Logo All rights reserved.
元のindec.pugのfooter部分は下記のとおり(パスに注意)
include ./parts/_footer.pug
下記はbody部分の抜粋
body
header.header
.header__inner.inner
h1.header__logo: a(href="./index.html") Logo
nav.header__nav
each menu in menus
a.header__link(href=menu.url) #{menu.name}
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
.section
.inner
h2.section__title セクション
.section__lead Section
.section__body
p トップページのコンテンツが入ります。
include ./parts/_footer.pug
例文:ヘッダー(メニューあり)をパーツ化する場合
header.pug全文の完成形は下記のとおり。変数宣言もindex.pugからカット&ペーストしてくる
-
const menus = [
{
name: "会社概要",
url: `${path}/company/index.html`
},
{
name: "お問い合わせ",
url: `${path}/contact/index.html`
},
];
header.header
.header__inner.inner
h1.header__logo: a(href="./index.html") Logo
nav.header__nav
each menu in menus
a.header__link(href=menu.url) #{menu.name}
配列定義(変数宣言)が別ファイル(index.pug)にある場合は、パーツファイル化する時に配列の後ろに「? menus : []」を付ける必要がある。
each menu in menus → each menu in menus ? menus : []
index.pugのheader部分は下記のとおり
include ./parts/_header.pug
元のindex.pugの全文は下記のとおり(インデントを間違えるとエラーになるため注意が必要)。
doctype html
html(lang="ja")
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
title DEMOサイト
meta(name="description" content=トップページのディスクリプション)
//- Font
// Font
link(rel="preconnect" href="https://fonts.googleapis.com")
link(rel="preconnect" href="https://fonts.gstatic.com" crossorigin)
link(href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700;900&display=swap" rel="stylesheet")
// CSS
link(rel="stylesheet" href="./assets/css/style.css")
body
include ./parts/_header.pug
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
.section
.inner
h2.section__title セクション
.section__lead Section
.section__body
p トップページのコンテンツが入ります。
include ./parts/_footer.pug
Pugでテンプレートファイルを作る
Pugでテンプレートファイルを使うとさらに効率化できます。
テンプレートを作る流れ
- srcフォルダにtemplateフォルダをつくる
- _page.pug(ファイル名は自由)を新規作成し、index.pugの中身をコピペ
- _page.pugの内容で変わる可能性あるところを変数に書き換えていく
- index.pugにテンプレートを使う宣言をする(extend テンプレートファイルへのパス)
- _page.pug トップでvalue(変数)を使う宣言(block value)
- _index.pug トップでvalue(変数)を使う宣言(append value)
- 上記append value下に変数宣言
srcフォルダにtemplateフォルダと_page.pugを作る
_page.pugはテンプレートファイルの名称です(命名は自由)。
DEMOサイト(フォルダ)
public(フォルダ)
assets(フォルダ)
CSS(フォルダ)
image(フォルダ)
src(フォルダ)
company(フォルダ)
contact(フォルダ)
parts(フォルダ)
template
_page.pug
index.html
index.pug
node_modules
gulpfile.js
package-lock.json
package.json
_page.pugにはindex.pugの内容をそのままコピペする
ヘッダーとフッターを読み込むincludeのパスは階層が変わるため、修正しないとコンパイルエラーになる。
include ./parts/_header.pug → include ../parts/_header.pug
_page.pug全文
doctype html
html(lang="ja")
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
title DEMOサイト
meta(name="description" content=トップページのディスクリプション)
//- Font
// Font
link(rel="preconnect" href="https://fonts.googleapis.com")
link(rel="preconnect" href="https://fonts.gstatic.com" crossorigin)
link(href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700;900&display=swap" rel="stylesheet")
// CSS
link(rel="stylesheet" href="./assets/css/style.css")
body
include ../parts/_header.pug
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
.section
.inner
h2.section__title セクション
.section__lead Section
.section__body
p トップページのコンテンツが入ります。
include ../parts/_footer.pug
_page.pug内要素の変数化
テンプレートファイル_page.pug内の変数化できる要素を変数化する
この例では、titleタグとmetaタグのmetaタグのdescriptionが変数化できる(ページによって変化するため)
- title DEMOサイト → title #{title}
- meta(name=”description” content=トップページのディスクリプション) → meta(name=”description” content=description)
以下、その部分の抜粋
html(lang="ja")
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
title #{title}
meta(name="description" content=トップページのディスクリプション)
_page.pugで変数を使う宣言
下記、一文を_page.pugトップに書く
block 変数名(自由)
_page.pugの例文抜粋
block value
doctype html
html(lang="ja")
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
title #{title}
meta(name="description" content=description)
//続く
index.pugにテンプレートを使う宣言をする(extend テンプレートファイルへのパス)
index.pugの1行目にテンプレートファイルを使う宣言を書く
extend テンプレートファイルへのパス
以下、今回の例文
extend ./template/_page.pug
index.pugで変数を使う宣言
下記一文をindex.pugに書く。
append 変数
- const 変数1 = "値1";
- const 変数2 = "値2";
この時、appendの後ろの変数名は、_page.pugのblockの後ろに付けた変数名と同じにする必要がある。
以下、今回のindex.pugの例文抜粋
extend ./template/_page.pug
append value
- const title = "DEMOタイトル";
- const description = "DEMOのディスクリプション";
コンテンツの変数化
さらにコンテンツも変数化するとコーディングが効率化します。
例えば、_page.pugのbody部分を例とした場合、元は下記のとおりですが…
body
include ../parts/_header.pug
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
.section
.inner
h2.section__title セクション
.section__lead Section
.section__body
p トップページのコンテンツが入ります。
include ../parts/_footer.pug
.main-visualと.section以下のブロックをそれぞれ変数化すると、下記のようになります。
たとえば、.main-visualのブロックの場合
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
上記ブロックが下記のように変数化できます。
block 変数名(命名は自由)
_page.pugのbody部分の抜粋は下記のとおりです。
body
include ../parts/_header.pug
block mainVisual
block content
include ../parts/_footer.pug
上記に対応するindex.pugは下記のようになります。
block 変数名(_page.pugで命名した名称)
コンテンツ
index.pugの全文
extend ./template/_page.pug
append value
- const title = "DEMOタイトル";
- const description = "DEMOのディスクリプション";
block mainVisual
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
block content
.section
.inner
h2.section__title セクション2
.section__lead Section
.section__body
p トップページのコンテンツが入ります。
上記のようにコンテンツもパーツファイル化しておくと、別のページにもテンプレートファイル(_page.pug)を共通で使うことができる。例えば、上の例では、「.main-visual__text トップページ」のうち「トップページ」はページによって異なる文字列が代入される。
下層ページにテンプレートファイルを使う場合
src/companyフォルダ内にある下層ページのindex.htmlの場合は、同じ階層内にindex.pugを新規作成し、トップページのindex.pugの内容をコピペする。続いて、変更箇所(テキスト、パス、クラス名など)のみを修正していく
index.pug(下層ページ)
extend ../template/_page.pug
append value
- const title = "会社概要|DEMOサイト";
- const description = "会社概要のディスクリプション";
block mainVisual
.main-visual
.main-visual__content
.main-visual__text 会社概要
block content
.section
.inner
h2.section__title 会社概要
.section__lead Company
.section__body
p 会社概要のコンテンツが入ります。
Pugのパスの変数化
パスを変数化して配列に加えることにより、ページごとのパスの設定を一部自動化できる。
今回のデモサイトの場合には、パスは./または../になるので.または..の部分を変数path(命名は自由)に置き換える。
header.pug
pug構文中に変数を入れる場合は書き方が特殊なので注意する
`${変数} …….`という書き方に変える。
変数化前(配列の定義文)
url: "./contact/index.html"
変数化後(配列の定義文)
url: `${path}/company/index.html`
以下、配列の定義部分の抜粋
-
const menus = [
{
name: "会社概要",
url: `${path}/company/index.html`
},
{
name: "お問い合わせ",
url: `${path}/contact/index.html`
},
];
変数化前(ヘッダーロゴのリンク先)
h1.header__logo: a(href="./index.html") Logo
header.header
.header__inner.inner
h1.header__logo: a(href="./index.html") Logo
nav.header__nav
each menu in menus
a.header__link(href=menu.url) #{menu.name}
変数化後(ヘッダーロゴのリンク先)
h1.header__logo: a(href=`${path}/index.html`) Logo
header.header
.header__inner.inner
h1.header__logo: a(href=`${path}/index.html`) Logo
nav.header__nav
each menu in menus
a.header__link(href=menu.url) #{menu.name}
_page.pug(テンプレートファイルのCSSパス)
変数化前(CSSパス)
// CSS
link(rel="stylesheet" href="./assets/css/style.css")
変数化後(CSSパス)
// CSS
link(rel="stylesheet" href=`${path}/assets/css/style.css`)
index.pug
index.pugでは下記のように配列に上で作ったパス変数pathを追記する
トップページ|index.pug
- const path = ".";
extend ./template/_page.pug
append value
- const title = "DEMOタイトル";
- const description = "DEMOのディスクリプション";
- const path = ".";
block mainVisual
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
block content
.section
.inner
h2.section__title セクション2
.section__lead Section
.section__body
p トップページのコンテンツが入ります。
下層ページ|index.pug
トップページとはパスが異なるので、../になるように値を入れる
- const path = "..";
extend ./template/_page.pug
append value
- const title = "DEMOタイトル";
- const description = "DEMOのディスクリプション";
- const path = "..";
Pugテンプレートを別のプロジェクトで使い回す方法
以上のPug記法はある程度テンプレート化し、コピペで使い回すことができるので、その方法を紹介します。
Pugのフォルダとファイル構成
下記のような作業用フォルダとファイルを作る。
作業用フォルダ
public(フォルダ) ←公開用フォルダ(ここにコンパイル後のファイルが生成される)
assets(フォルダ)
CSS(フォルダ)
image(フォルダ)
src(フォルダ) ←開発用フォルダ
company(フォルダ)
index.html
index.pug ←下層ページ
contact(フォルダ)
index.html
index.pug ←下層ページ
parts(フォルダ)
_header.pug ←共通パーツファイル
_footer.pug ←共通パーツファイル
template
_page.pug ←共通テンプレートファイル
index.html
index.pug ←トップページ
gulpfile.js ←コピペで作る
package.json ←コピペで作る
gulpfile.jsのテンプレ
gulpfile.jsには下記内容をコピペすれば、そのまま使えます。
const gulp = require("gulp");
const pug = require("gulp-pug");
function compilePug() {
// 出力元のパス
return gulp.src("./src/**/*.pug", "!./src/**/_*.pug")
// コンパイルの処理を書く
.pipe(pug({
pretty: true
}))
// 出力先のパス
.pipe(gulp.dest("./public"))
}
exports.compilePug = compilePug;
package.jsonのテンプレ
package.jsonには下記内容をコピペすれば、そのまま使えます。
{
"name": "drive-download-20230504t004345z-001",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"gulp": "^4.0.2",
"gulp-pug": "^5.0.0"
}
}
Pugのインストール
続いて、VSCodeでターミナルを開き、下記コマンドを入力リターンするとPugのインストールが行われPugが使える状態になります。
npm install
具体的には、node_modulesフォルダとpackage-lock.jsonファイルが生成されるはずです。
テンプレートファイル
以下に紹介するのテンプレートファイルの雛形です。必要に応じて変更が必要です。
これらのファイルを作った状態で、VSCodeのターミナルに下記コマンドを入力リターンすると、publicフォルダ内にPugからコンパイルされたhtmlファイルが自動生成されます。
npx gulp compilePug
index.pug|トップページ
extend ./template/_page.pug
append value
- const title = "DEMOタイトル";
- const description = "DEMOのディスクリプション";
- const path = ".";
block mainVisual
.main-visual.is-top
.main-visual__content
.main-visual__text トップページ
block content
.section
.inner
h2.section__title セクション2
.section__lead Section
.section__body
p トップページのコンテンツが入ります。
index.pug|下層ページ
extend ../template/_page.pug
append value
- const title = "会社概要|DEMOサイト";
- const description = "会社概要のディスクリプション";
- const path = "..";
block mainVisual
.main-visual
.main-visual__content
.main-visual__text 会社概要
block content
.section
.inner
h2.section__title 会社概要
.section__lead Company
.section__body
p 会社概要のコンテンツが入ります。
_header.pug|共通のヘッダーパーツ
-
const menus = [
{
name: "会社概要",
url: `${path}/company/index.html`
},
{
name: "お問い合わせ",
url: `${path}/contact/index.html`
},
];
header.header
.header__inner.inner
h1.header__logo: a(href=`${path}/index.html`) Logo
nav.header__nav
each menu in menus
a.header__link(href=menu.url) #{menu.name}
_footer.pug|共通のフッターパーツ
footer.footer
.footer__inner.inner
.footer__copyright Copyright © 2020 Logo All rights reserved.
_page.pug|共通のテンプレートパーツ
block value
doctype html
html(lang="ja")
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
title #{title}
meta(name="description" content=description)
//- Font
// Font
link(rel="preconnect" href="https://fonts.googleapis.com")
link(rel="preconnect" href="https://fonts.gstatic.com" crossorigin)
link(href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700;900&display=swap" rel="stylesheet")
// CSS
link(rel="stylesheet" href=`${path}/assets/css/style.css`)
body
include ../parts/_header.pug
block mainVisual
block content
include ../parts/_footer.pug