今回は、ページごとに HTML ファイルがあるような従来のサイトを作るときに役立つ EJS というテンプレート言語を、Mix で使う方法をご紹介します。
EJS とは
Embedded JavaScript の略で、通常の HTML に JavaScript を組み合わせることができるテンプレート言語です。include もできるので、ヘッダーやフッターの HTML を使い回すことができます。
Laravel Mix で使う
以下のファイルをダウンロードして、webpack.mix.js と同じ階層に置いてください。拡張子が「txt」となっているので、「js」に変更してください。
そして、webpack.mix.js でこのファイルを require します。変数に代入する必要はありません。
const mix = require('laravel-mix');
require('./ejs-component');
使い方
.ejs メソッドが使えるようになっています。以下は、resources/ejs フォルダ内の .ejs ファイル(_ から始まるものを除く)を public フォルダに書き出す設定です。
mix.ejs('resources/ejs/**/*.ejs', 'public', {
basePath: 'resources/ejs',
ignore: ['**/_*.ejs'],
})
第1引数は、変換対象のファイル(glob 形式)です。
第2引数は、出力先のフォルダです。
第3引数には、オブジェクトを渡します。以下のプロパティの他に、HTML Webpack Plugin のオプションを指定することができます。
basePath | 第1引数の内、第2引数で置き換えたい文字を指定します |
ignore | 第1引数で指定したファイルの内、変換したくないファイル(glob 形式)を配列で指定します |
inject | 書き出す HTML に自動でアセットファイルへのリンクを張ります。HTML Webpack Plugin のオプションの一つ |
また、EJS ファイルの拡張子を「.php.ejs」とすることで、PHP ファイルを出力することができます。
EJS を書き出してみる
まずは簡単な EJS を HTML に変換してみましょう。webpack.mix.js を編集します。
mix
.setPublicPath('public')
.ejs('resources/ejs/**/*.ejs', 'public', {
basePath: 'resources/ejs',
ignore: ['**/_*.ejs'],
})
.browserSync({
files: ['public/**/*'],
server: 'public',
proxy: false,
});
setPublicPath はその名の通り、公開フォルダを指定します。必須ではありませんが、設定しておくことをお勧めします。
resources/ejs フォルダの中に index.ejs を作成します。
<!DOCTYPE html>
<%
const metaVars = ['hoge', 'piyo', 'fuga'];
%>
<title>ejs test</title>
<body>
<% if (metaVars.length) { %>
<% for (let metaVar of metaVars) { %>
<p><%= metaVar %></p>
<% } %>
<% } else { %>
<p>配列が空です。</p>
<% } %>
</body>
ここまでできたら npm run watch を実行します。metaVars の中身が一行ずつ表示されているでしょう。
metaVars を空の配列にしてコンパイルすると、条件分岐により「配列が空です。」と表示されます。
EJS タグについて
ここでは EJS の主なタグを紹介します。他のタグも知りたい場合は EJS 公式ページの Tags を参照してください。
<%= %> | 内容をエスケープしてその場に出力する |
<%- %> | 内容をエスケープせずにその場に出力する |
<% %> | JavaScript を書くときに使用する。内容は出力されない |
<%# %> | コメントを書くときに使用する。内容は出力されない |
EJS で何かを書き出すときは、基本的に <%= %> タグを使いましょう。
自分で書いた HTML を出力するなど、安全であることが確信できる場合のみ <%- %> タグを使います。
EJS の include を使う
EJS では、他の EJS ファイルの内容を取り込むことができます。例えば _header.ejs を作っておけば、もし共通で使うヘッダーの HTML を変更したくなっても、このファイルの修正だけで済みます。
ejs メソッドに ignore を指定することで、include 用の EJS が個別に書き出されてしまうのを防ぐことができます。
ここでは例として、共通のヘッダー・サイドバー・フッターとなる EJS を作成し、include してみます。Mix 設定は以下のようにします。
mix
.setPublicPath('public')
.sass('resources/sass/app.scss', 'public/css')
.ejs('resources/ejs/**/*.ejs', 'public', {
basePath: 'resources/ejs',
ignore: ['**/_*.ejs'],
})
.options({
postCss: [
require('autoprefixer')({
grid: 'autoplace',
}),
]
})
.browserSync({
files: ['public/**/*'],
server: 'public',
proxy: false,
});
次に、それぞれ EJS を作成します。
<!DOCTYPE html>
<title>include test</title>
<link rel="stylesheet" href="/css/app.css">
<body>
<div id="grid-page">
<div id="page-header">
<%- include('_layouts/_header') %>
</div>
<div id="page-main">
<div role="main" class="middle-center">
main
</div>
</div>
<div id="page-sidebar">
<%- include('_layouts/_sidebar') %>
</div>
<div id="page-footer">
<%- include('_layouts/_footer') %>
</div>
</div>
</body>
<header id="header" class="middle-center">
_header.ejs
</header>
<div id="sidebar" class="middle-center">
_sidebar.ejs
</div>
<footer id="footer" class="middle-center">
_footer.ejs
</footer>
最後に、app.scss を編集します。
#grid-page {
display: grid;
height: 20rem;
grid-template:
"header header " 3rem
"main sidebar" 1fr
"footer footer" 4rem
/1fr 200px;
#page-header { grid-area: header }
#page-main { grid-area: main }
#page-sidebar { grid-area: sidebar }
#page-footer { grid-area: footer }
}
#header {
background-color: mediumseagreen;
}
#sidebar {
background-color: gainsboro;
}
#footer {
background-color: black;
color: white;
}
.middle-center {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
これで npm run watch すれば、以下のような画面になるかと思います。
include のパスに注意
新しく resouces/ejs/hoge フォルダを作成し、先程の index.ejs をコピーしてみましょう。
コンパイルしようとすると、以下の箇所でエラーが発生します。
<div id="page-header">
<%- include('_layouts/_header') %>
</div>
エラーを防ぐには include('../_layouts/_header') のようにすれば良いのですが、これでは階層が変わるたびに include に渡す文字列を変更しなければなりません。
そこで、EJS 側で階層を定義しておくと、この include タグを使い回せるようになります。
<!DOCTYPE html>
<%
const conf = {
hrchy: '../',
};
%>
<title>include test</title>
<link rel="stylesheet" href="/css/app.css">
<body>
<div id="grid-page">
<div id="page-header">
<%- include(`${conf.hrchy}_layouts/_header`) %>
</div>
<div id="page-main" class="middle-center">
main
</div>
<div id="page-sidebar">
<%- include(`${conf.hrchy}_layouts/_sidebar`) %>
</div>
<div id="page-footer">
<%- include(`${conf.hrchy}_layouts/_footer`) %>
</div>
</div>
</body>
トップなら「./」、第3階層なら「../../」というふうに設定します。また、このようにしておくことで、相対パスでリンクや画像をはりたいという場合にも対応できます。
<!-- public/images/hoge.png を貼る場合-->
<img src="<%= conf.hrchy %>images/hoge.png" alt="">
また、include の第2引数にオブジェクトを渡すと、include される EJS にデータを渡すことができます。先程定義した conf を渡しておきましょう。
<%- include(`${conf.hrchy}_layouts/_header`, { conf }) %>
<%- include(`${conf.hrchy}_layouts/_sidebar`, { conf }) %>
<%- include(`${conf.hrchy}_layouts/_footer`, { conf }) %>
こうすることで、conf を _header.ejs や _footer.ejs でも利用できるようになります。