ECT等のテンプレートエンジンについては、弊社ブログでもその導入方法や使い方について既に何度か取り上げていますが、今回はちょっと立ち戻ってのそもそも論から、誰にも聞かれていない個人的な使い方までご紹介したいと思います。
G先生を介して、テンプレートエンジンとはと諸先輩方に聞いてみたところ、
「データとテンプレートを合体させ、文字列を作る仕組みのこと」
と教えていただいたのですが、なんだかよくわからなかったので、今回ご紹介するところのテンプレートエンジンについての自分なりの概念図を作ってみました。
世にテンプレートエンジンは多々ありますが、大体こんなかんじの概念かと思います(たぶん)。
構成(レイアウト)ファイルを各ページでextend(継承)し、「title」「body」という構成要素の中身だけをページごとに設定(コーディング)していくイメージです。
もちろん、headerやfooter等、共通化できるコンテンツはモジュールとしてパーツ化することで、各ページにいちいち長いコードを書く必要がなくなり、そしてここが真骨頂→もし修正が入った場合にそのモジュールファイルさえ直せば、読み込んでいる全ページにおいてするっとまるっと修正できるのです。
いままでは、Dreamweaverのサイト管理なんかでわざわざ修正したり、なんなら全ページ開いてひたすら検索&置換するとかいうことで賄っていた作業が一瞬で終わるという、嗚呼「素晴らしき哉、効率化!」
ということで次は、弊社で使われている2大テンプレートエンジン、ECT&JADEについてご紹介します。
SAMPLE
- <article>
- <header id="header">
- <div class="inner">
- <h1 class="title"><%- @title %></h1>
- <ul class="btns">
- <li class="btn"><a href="<%- @url %>map/">map</a></li>
- <li class="btn"><a href="<%- @url %>contact/">contact</a></li>
- </ul>
- </div>
- </header>
- </article>
普通にコーディングしていく中で、モジュール化や定数を設定したりが簡単に行なえるので非常に導入しやすそうです。
SAMPLE
- article
- header#header
- div.inner
- h1.title= _const_title
- ul.btns
- li.btn: a(href="#{const_url}map/") map
- li.btn: a(href="#{const_url}contact/") contact
ECT、JADEそれぞれの「SAMPLE」をコンパイルすると全く同じ出力結果になるのですが、JADEのほうがだいぶ記述を簡略化できることがわかります。
クセはありますが、キーボードのタッチ数?が確実に減るので指にも優しいですね。
ここからは、ごく個人的なテンプレートモジュールの使い方をご紹介していきます。
私がテンプレートエンジンを使いたい理由として、
がありまして、「HTMLタグで普通に書きたい」と宣っている時点で私が使うべきは完全にECT一択となっております。
ではまず、ディレクトリ構造から。
- ect/
- └ _include/
- ├ _head.ect
- ├ _header.ect
- .
- .
- └ _setting/
- └ _variables.js
- └ htdocs/
- ├ index.ect
- └ hoge/
- ├ index.ect
- ├ hoge.ect
- └ hogehoge/
- ├ index.ect
- .
- .
次に、上記ECTファイルと、実際MovableType(以下MT)に移植した場合の中身を比較して見てみます。
まずは、読み込み専用の定数設定ファイルから。
MTを構築する際、弊社では(というか一般的にそうなんでしょうか?)サイトのテンプレートモジュールに各ページ(各テンプレート)におけるtitleタグやdescription等、変数や定数をある程度設定しまくったファイルを置くのが基本なので、それに移植しやすいよう作りたいなと思っている次第です。
_setting/_variables.js
- var config = {
- websiteName: 'サイト名',
- websiteURL: 'サイトURL',
- websiteDescription: 'サイトdescription'
- };
- module.exports = config;
テンプレートモジュール > [conf] websiteSetting
- <mt:SetVars>
- websiteName=<$mt:WebsiteName$>
- websiteURL=<$mt:WebSiteURL$>
- websiteDescription=<$mt:WebsiteDescription$>
- websiteURL_ROOT=/
- </mt:SetVars>
次に、実際に出力されるページの中身を見てみます。
htdocs/index.ect
- <% param = { page: 'top', dir: '1' } %>
- <% path = '' %>
- <% block 'path': %><% end %>
- <% block 'title': %><%- @websiteName %><% end %>
- <% block 'pageURL': %><%- @websiteURL %><% end %>
- <% block 'description': %><%- @websiteDescription %><% end %>
- <% block 'head_css': %>
- <link rel="<% content 'path' %>css/top.css">
- <% end %>
- <% block 'foot_js': %>
- <script src="<% content 'path' %>js/top.js"></script>
- <% end %>
- <% include path + '../_include/head.ect', param %>
- <% include path + '../_include/header.ect', param %>
- .
- .
- .
1行目→paramと名付けたパラメータに、ページの情報(このページがなんたるか、カテゴリとか階層とか)を設定して、_header.ect等をインクルードする際に投げてあげると、インクルードファイル内でifの分岐処理ができて便利です。
インデックステンプレート > メインページ
- <$mt:SetVar name="top" value="1"$>
- <$mt:Include module="[conf] websiteSettings" parent="1"$>
- <mt:SetVarBlock name="head_css">
- <link rel="<$mt:Var name="websiteURL_ROOT"$>css/top.css"></link>
- </mt:SetVarBlock>
- <mt:SetVarBlock name="foot_js">
- <script src="<$mt:Var name="websiteURL_ROOT"$>js/top.js"></script>
- </mt:SetVarBlock>
- <$mt:Include module="[inc] Head" parent="1"$>
- <$mt:Include module="[inc] Header" parent="1"$>
- .
- .
- .
このページだけで読み込むべきCSSやJS等は大体こんなかんじで設定しています。
ECTからも大体そのまま移植できるようにします。
最後、インクルードするモジュールファイルです。
_include/header.ect
- <header class="header" id="data-header">
- <<% if @page is 'top': %>h1<% else: %>span<% end %> class="header-logo">
- <a href="<% content 'path' %>"><img src="<% content 'path' %>images/common/logo_header.svg" alt="<%= @websiteName %>" class="header-logo-image"></a>
- </<% if @page is 'top': %>h1<% else: %>span<% end %>>
- <div class="header-btn_nav" id="data-nav-btn"><span class="ico">menu</span></div>
- </header>
<% content 'path' %>では、htdocsの各ECTで<% block 'path': %><% end %>として設定したルートからの相対パスが入る想定です。ex) ../
こうすることで、どの階層のECTファイルからインクルードしてもリンクが繋がります。
テンプレートモジュール > [inc] Header
- <header class="header" id="data-header">
- <<mt:If name="top">h1<mt:Else>span</mt:Else></mt:If> class="header-logo">
- <a href="<$mt:Var name="websiteURL_ROOT"$>"><img src="<$mt:Var name="websiteURL_ROOT"$>images/common/logo_header.svg" alt="<$mt:Var name="websiteName "$>" class="header-logo-image"></a>
- </<mt:If name="top">h1<mt:Else>span</mt:Else></mt:If>>
- <div class="header-btn_nav" id="data-nav-btn"><span class="ico">menu</span></div>
- </header>
ECTファイルとMTテンプレートを同じ構造にしておけば、誰が見ても大体理解でき、「みなまで言うな」的に引き継げますね。ハイ効率化効率化!
なお、とっくにお気づきかと思いますが、MTにそういう機能がなかったもので、実は所謂extendファイルというものを使っておりませんすみません。。。
最近、弊社の開発環境がGruntからgulpへ移行したのですが、grunt-ectでやっていたことがgulp-ectだとできない...という憂き目に遭って困ったので(gulp ECTは2014年から更新が止まっているのです。※2016年8月現在)、ここまで書いておいてなんですが、
「EJSにも手を出してみようかな」
などと密かに思っていることをお伝えして締めの言葉とさせていただきます。
ご清覧ありがとうございました!