gulp4 + EJS でウェブサイトをパーツ化して修正の手間を少なくする
Table of Contents
静的ウェブサイトを効率よく作りたい
30ページ程度のサイトでも、ヘッダーとフッターの修正が入る度に全ページに影響するので大変ですよね。
大規模になるとCMSを使うことが多いのでしょうが、小規模だととりあえず静的に作ってしまうこともあると思います。そんな場合のサイト作成を楽にするにはEJSが一番手っ取り早いと思いました。
EJSはJavascriptのテンプレートエンジンで、gatsbyとかvue.jsとかフレームワークまでは必要ないときにはちょうど良いと思うものでした。
webフレームワークは、フロントエンドのみだとちょっと大袈裟じゃないですかね。
クラス名とかよくわからない名前になったりするし。
htmlって普通にincludeできたらいいのにな。
EJSを使って共通部品化する
ヘッダーやフッターを分割し部品としてincludeすることで、ナビゲーションなど共通部分の修正が1ファイルで済みます。
こんな感じ。
<%
const pageData = sitedata.page1;
const rootPath = pageData.path.replace(/[a-z]+/g,'..').replace('/','');
%>
<%# <DOCTYPE>から</head>まで %>
<%- include(rootPath + 'components/_head',{ meta: pageData }); %>
<body>
<%# <header>タグ %>
<%- include(rootPath + 'components/_header', {data: pageData }); %>
<%# ここからページのコンテンツ %>
<main>
<article>
<section>
<h2>ページ コンテンツ</h2>
</section>
</article>
<%# 共通バナー %>
<%- include(rootPath +'components/_banner', { bannerData: pageData.banner }); %>
</main>
<%# <footer>タグ %>
<%- include(rootPath + 'components/_footer'); %>
</body>
</html>
ついでに、ページ毎に変更するmetaタグやページタイトルなどはjsonファイルにまとめてあり、ページタイトルやリンクパス変更はjsonファイルの書き換えだけで済むようにしました。
いろんなファイルを開かなくて済むのは良いことです。
一括置換でもいいですが、たまに思いもよらぬ場所を置換してたりするので・・・。
準備
まず、Node.jsがインストールされていること
Node.jsのインストールはいろんな方が書いているので割愛。
$ node -v
v15.0.1
たぶん、入ってれば大丈夫。
ディレクトリ構成
今回必要なディレクトリは以下
website | ||
dist | EJSコンパイル先ディレクトリ(ドキュメントルート) | |
css | sassコンパイル先 | |
js | minify先js | |
src | コンパイル元ディレクトリ | |
ejs | EJSファイル群 | |
common | EJS共通部品 | |
data.json | EJSで使うjsonファイル | |
scss | sassファイル | |
js | jsファイル |
サイト作成で手を入れるのは、src配下のファイルのみです。
distはファイルの出力先で、「dist = ドキュメントルートに配置するファイル群」となります。
publicっていう名前にした方がわかりやすいかもしれない。
websiteディレクトリに移動して、インストールしていきましょう。
gulpインストール
npmでふわっとインストール。必要なモジュールもまとめて入れてしまえばいいと思います。
$ cd website
$ npm init -y
$ npm i -D gulp
$ npm i -D gulp-notify gulp-plumber gulp-sass sass gulp-autoprefixer gulp-uglify gulp-uglify browser-sync gulp-rename gulp-ejs gulp-replace gulp-sitemap
必要になるディレクトリもよっこらしょっと、作っておきましょう。
mkdir -p {dist/css,dist/js,src/ejs,src/ejs/common,src/scss,src/js}
この後作成するgulpfile.jsに合わせた名前なので、違うフォルダ名にするときはgulpfile.jsの設定内容のも変更する必要があります。
gulpfile.jsを作成
websiteフォルダ直下に作成します。
含まれる機能(タスク)
- EJSコンパイル
- src/ejs のファイルを distにhtmlファイルで出力する
- sassコンパイル
- src/scssのファイルをdist/cssに出力する
- jsのminify
- src/jsのファイルをdist/js/*.min.jsにして出力する
- 自動ブラウザ
- localhost:8001でwebサービスを立ち上げてブラウザで確認できる
- ファイルの更新を監視する
- srcフォルダ以下のファイルが保存されたら自動でタスクを実行する
- おまけ
- sitemap.xmlの出力(watchしていないので手動で実行する)
website/gulpfile.js
const gulp = require('gulp');
const notify = require("gulp-notify");
const plumber = require("gulp-plumber");
const sass = require('gulp-sass')(require('sass'));
const autoprefixer = require('gulp-autoprefixer');
const uglify = require('gulp-uglify');
const browserSync = require("browser-sync");
const rename = require('gulp-rename');
const ejs = require('gulp-ejs');
const replace = require('gulp-replace');
const fs = require('fs');
const sitemap = require('gulp-sitemap');
//パス設定
const paths = {
'src' : {
'html' : './src/',
'scss' : './src/scss/',
'js' : './src/js/',
'ejs' : './src/ejs/'
},
'dist' : {
'html' : './dist/',
'css' : './dist/css',
'js' : './dist/js'
}
}
const files = {
'html' : '**/*.html',
'ejs' : '**/*.ejs',
'ejs_partial' : '!./src/ejs/**/_*.ejs',
'scss' : '**/*.scss',
'css' : '**/*.css',
'js' : '**/*.js',
'jsondata' : './src/ejs/data.json'
}
// EJSコンパイル
const compileEjs = (done) => {
const json = JSON.parse(fs.readFileSync(files.jsondata), 'utf8');
gulp.src([paths.src.ejs+files.ejs, files.ejs_partial])
.pipe(plumber())
.pipe(ejs({data:json}, {}, { ext: '.html' }))
.pipe(rename({ extname: '.html' }))
.pipe(replace(/^[ \t]*\n/gmi, ''))
.pipe(gulp.dest(paths.dist.html));
done();
};
// sassコンパイル
const compileSass = (done) => {
gulp.src(paths.src.scss + files.scss)
.pipe(sass({
outputStyle: 'expanded'
})
)
.on('error', sass.logError)
.pipe(autoprefixer())
.pipe(gulp.dest(paths.dist.css));
done();
};
// JS圧縮
const minifyJs = (done) => {
gulp.src(paths.src.js + files.js)
.pipe(plumber())
.pipe(uglify())
.pipe(rename({extname: '.min.js'}))
.pipe(gulp.dest(paths.dist.js));
done();
};
// リロードするhtml
const reloadFile = (done) => {
browserSync.init({
server : {
baseDir : paths.dist,
index : 'index.html',
},
port : 8001,
});
done();
};
// リロード設定
const reloadBrowser = (done) => {
browserSync.reload();
done();
};
// sitemap
const generateXML = function(done){
gulp.src('./dist/**/*.html',{
read: false
})
.pipe(sitemap({
siteUrl: 'https://example.com'
}))
.pipe(replace(/^[ \t]*\n/gmi, ""))
.pipe(gulp.dest('./dist'));
done();
}
// タスク化
exports.compileEjs = compileEjs;
exports.compileSass = compileSass;
exports.minifyJs = minifyJs;
exports.reloadFile = reloadFile;
exports.reloadBrowser = reloadBrowser;
exports.generateXML = generateXML;
// 監視ファイル
const watchFiles = (done) => {
gulp.watch([paths.src.ejs+files.ejs,files.jsondata], compileEjs);
gulp.watch(paths.dist.html+files.html, reloadBrowser);
gulp.watch(paths.src.scss+files.scss, compileSass);
gulp.watch(paths.dist.css+files.css, reloadBrowser);
gulp.watch(paths.src.js+files.js, minifyJs);
gulp.watch(paths.dist.js+files.js, reloadBrowser);
done();
};
// タスク実行
exports.default = gulp.series(
watchFiles, reloadFile, compileEjs, compileSass, minifyJs, generateXML
);
ここまでのディレクトリ構成はこうなってるはず
website | ||
dist | EJSコンパイル先ディレクトリ(ドキュメントルート) | |
css | sassコンパイル先 | |
js | minify先js | |
gulpfile.js | 作成したgulpfile.js | |
node_modules | npmでインストールしたパッケージ | |
package-lock.json | npmでインストールしたパッケージの情報 | |
package.json | npmでインストールしたパッケージの情報 | |
src | コンパイル元ディレクトリ | |
ejs | EJSファイル群 | |
common | EJS共通部品 | |
data.json | EJSで使うjsonファイル | |
scss | sassファイル | |
js | jsファイル |
さて、このままgulpを動かしても必要なファイルがなくて怒られてしまうので、最低限のファイルを作ってしまいましょう。
まず、index.htmlとなるファイル
src/ejsフォルダ直下に作成します。拡張子は.ejsです。
website/src/ejs/index.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<%# dataがdata.jsonを読み込んだオブジェクト %>
<title><%= data.title %></title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<!----- header----->
<header>
ヘッダー
<nav>ナビ</nav>
</header>
<!----- /header ----->
<!----- main ----->
<main>
<article>
<h1><%= data.title %></h1>
<section>
<h2>見出し2</h2>
<p>コンテンツの内容</p>
</section>
</article>
</main>
<!----- /main ----->
<!----- footer ----->
<footer>
フッター
</footer>
<!----- /footer ----->
</body>
</html>
src/scssにもファイルを追加しましょうか。
website/src/scss/style.scss
@charset "utf-8";
$bgcolor: #eee;
body{
background-color: $bgcolor;
}
data.jsonは、とりあえずindex.ejsでタイトルを読み込みたいのでtitleを取得できるように。
website/src/ejs/data.json
{
"title": "サイトタイトル"
}
gulpを実行
$ gulp
watchしているので、ファイルを修正するとコンパイルしてリロードするようになります。
distにファイルが生成されていますね。
gulpはctrl+cで止めるまで動いているので、試しにscssを変更して保存してみるとgulp実行時に立ち上がったブラウザが勝手にリロードしているのが確認できはずです。
ヘッダー、フッターを部品にする
ここで、ヘッダー・フッターを共通部品にするために、_header.ejs、_footer.ejsを作成します。
ファイル名の最初がアンダーバー_のファイルは出力されません。
私はcommonフォルダ下に置いていますが、ejsフォルダ以下であればどこに置いても構いません。
website/src/ejs/common/_header.ejs
<header>
共通ヘッダー
<nav>共通ヘッダナビ</nav>
</header>
website/src/ejs/common/_footer.ejs
<footer>
共通フッター
</footer>
そして、index.ejsのヘッダー、フッターを共通部品のインクルードに変更します。
website/src/ejs/index.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<%# dataがdata.jsonを読み込んだオブジェクト %>
<title><%= data.title %></title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<!----- header----->
<%- include('common/_header'); %>
<!----- /header ----->
<!----- main ----->
<main>
<article>
<h1><%= data.title %></h1>
<section>
<h2>見出し2</h2>
<p>コンテンツの内容</p>
</section>
</article>
</main>
<!----- /main ----->
<!----- footer ----->
<%- include('common/_footer'); %>
<!----- /footer ----->
</body>
</html>
フォルダを追加してもう一つページを作り、共通部品を使ってみましょう。
ここで注意しなければならないのが、EJSのincludeする部品のパスです。
ejsファイル自身からの相対パスで指定しています。
website/src/ejs/page1/index.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<%# dataがdata.jsonを読み込んだオブジェクト %>
<title>page1 | <%= data.title %></title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<!----- header----->
<%- include('../common/_header'); %>
<!----- /header ----->
<!----- main ----->
<main>
<article>
<h1>Page1</h1>
<section>
<p>Page1 コンテンツ</p>
</section>
</article>
</main>
<!----- /main ----->
<!----- footer ----->
<%- include('../common/_footer'); %>
<!----- /footer ----->
</body>
</html>
これを保存すると、distにもpage1フォルダが追加されindex.htmlも出力されています。
共通部品も読み込まれているので、ヘッダー、フッターはそれぞれのファイルを修正するだけで全てのページが更新されるようになりました。
サイトマップを作るには、
$ gulp sitemap
です。dist直下に、sitemap.xmlを出力します。
一度設定してしまえば、ラクラクです。
部品をインクルードするときに変数を渡したり、jsonから取得したり、結構やりたい放題です。
私は、jsonのデータは定数扱いですね。
関連リンク