原文:http://fraserxu.me/2013/09/12/Using-Handlebarsjs-with-Expressjs/javascript
最近在用Expressjs作一個項目,先後端都用它來完成。本身以前有用過Express一段時間,可是大部分都是用它來編寫Restful的API,而沒有真正用它所提供的前端頁面渲染功能。css
因此嚴格意義來說這是第一次完整的項目。開始作以後就遇到了一些須要作出決定的地方。衆所周知,Express的默認模板引擎是Jade.我在以前學習Express的時候,由於它是默認的引擎,因此有接觸和使用過一段時間,感受也還行。Jade在編寫頁面時所提供的嵌套功能比較實用,能夠節省很大的代碼量。html
Jade is a high performance template engine heavily influenced by Haml and implemented with JavaScript for node. For discussion join the Google Group.前端
上面是Jade Github所在頁面的描述。能夠得知它是一個注重性能,受Hamle影響,並特別針對Nodejs而編寫的前端模板引擎。java
咱們先來看一下Jade官方頁面所給的例子:node
doctype 5
html(lang="en")
head
title= pageTitle
script(type='text/javascript').
if (foo) {
bar(1 + 5)
}
body
h1 Jade - node template engine
#container.col
if youAreUsingJade
p You are amazing
else
p Get on it!
p.
Jade is a terse and simple
templating language with a
strong focus on performance
and powerful features.
咱們能夠看到,對比原生的HTML, Jade明顯的一個優點就是標籤數量上的減小。不少地方只要按照約定的縮進規則編寫,徹底能夠避免使用原生HTML時標籤忘記閉合的問題。同時Jade還提供了一些用於渲染判斷的條件,能夠根據數據來決定顯示的內容等功能。git
另外Jade的遍歷數據生成頁面功能,配合使用Json數據時特別好用,能夠很大程度上減小代碼量。github
而另一個緣由,也是覺大多數人使用Jade的緣由,可能都跟我同樣,由於是Express框架自帶的模板引擎,而它的做者也是鼎鼎有名的TJ.express
看了標題也許會奇怪,既然Jade出自大神之手,並且簡單易用,我爲何還要去選擇Handlebarsjs呢?npm
一樣咱們看下官方描述:
Handlebars provides the power necessary to let you build semantic templates effectively with no frustration.
Mustache templates are compatible with Handlebars, so you can take a Mustache template, import it into Handlebars, and start taking advantage of the extra Handlebars features.
做爲一個模板引擎,它繼承於著名的Mustache模板引擎,具有了渲染頁面的基礎功能,並在其基礎上進行拓展。
而另外一個值得關注的是其做者Yehuda Katz,熟悉的朋友可能知道,他是著名JavaScript MVC框架Emberjs代碼的主要貢獻者之一,並且在他的影響下也成爲了Emberjs的默認模板引擎。而另外,Yehuda自己也是W3C規範制定小組的成員之一,其影響也不亞於TJ.
拋開框架的背景,咱們來看看實際的應用場景。工具無非好壞,順手纔是王道。評斷一個東西好壞關鍵仍是看它是否知足本身的應用需求。
在開始作如今的項目以前,我已經用Jade完成了全部的功能,並且對於代碼也還比較滿意。可是在提交以後問題產生了。
由於這個項目不是我一我的在作,和我一塊兒合做的同事以前沒有接觸過Jade,並且另一位負責編寫樣式的同事對於JavaScript的模板引擎也不是很熟悉。這樣一來,因爲個人緣由,致使團隊成員之間沒法協做。首先是JS開發人員須要時間來掌握和熟悉Jade語法,而另一個更爲嚴重,Jade語法的特性決定了其不利於配套CSS的書寫(這點經過編譯以後能夠解決,可是必定程度上增長了工做量)。
因而我開始思考使用Jade是否正確。這裏的兩個問題是我必須面對的,而項目的進度不能由於這個受到影響,因而我開始考慮選擇其餘的模板引擎。
前面提到Emberjs用到了Handlebarsjs,因此在選擇時我很容易就想到了它。
Handlebars的官網給出了不少例子,並且上手也很容易,先後端通用,使用起來也很簡單,這裏就不對其使用多作介紹。
回到文章重點,由於Express並不提供對Handlerbarjs的直接支持,這樣在使用時會面臨必定問題。
要在Express中使用Handlerbars做爲模板引擎,首先須要作出一下設置:
安裝Express, Handlebars, Consolidate:
"dependencies": { "express": "3.x", "consolidate": "0.4.0", "handlebars": "1.0.7" }
配置選擇引擎:
// Use handlebars as template engine app.engine("html", consolidate.handlebars); app.set("view engine", "html"); app.set("views", __dirname + "/views");
註冊模板:
// Register partials var partials = "./views/partials/"; fs.readdirSync(partials).forEach(function (file) { var source = fs.readFileSync(partials + file, "utf8"), partial = /(.+)\.html/.exec(file).pop(); Handlebars.registerPartial(partial, source); })
這樣咱們就能夠在項目中使用Handlerbars來渲染頁面。可是這樣作後,我又遇到了另一個問題。經過以上的方法我能夠很容易的單獨去加載某個頁面。可是實際應用中,通常會有多個頁面,並且多個頁面之間會共享頁面的header和footer部分。這樣會致使重複編寫不少代碼。
在使用Jade是咱們能夠很容易的使用以下代碼來實現頁面模板功能:
include layout
可是因爲Express並不是直接支持Handlerbars,因此要實現這個功能還須要必定的設置。在Handlerbars中,能夠經過 來實現sub-template的功能。在查找了相關模塊以後,我發現了hbs這個Express中間件。
這個模塊使用起來很簡單,能夠完美解決我所遇到的問題。使用方法以下:
安裝模塊:
npm install hbs --save
設置模板:
app.set('view engine', 'html');
app.engine('html', require('hbs').__express);
註冊模板:
var hbs = require('hbs'); hbs.registerHelper('helper_name', function(...) { ... }); hbs.registerPartial('partial_name', 'partial value');
若是須要註冊整個文件夾,也可以使用以下命令:
var hbs = require('hbs'); hbs.registerPartials(__dirname + '/views/partials');
這樣,咱們就能夠作到頁面模板的重複利用,能夠顯著減小代碼量。
而另一個關鍵緣由,在於Handlerbars對比Jade,語法更加簡單。最重要的仍是其普通元素一樣使用原生HTML的寫法,這樣,對於編寫樣式的同事來說就會更加友好。使用傳統的方式編寫樣式,能夠顯著下降學習成本,從而加快項目進度。
而Handerbars所帶來的一些其餘功能,也會讓項目的開發變得更加輕鬆。
下面附上我項目的基本結構,但願能對一樣使用這種方案的同窗有必定幫助。
.
├── app.js
├── node_modules
│ ├── express
│ ├── handlebars
│ ├── hbs
│ ├── less-middleware
│ ├── nodemon
│ └── request
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ │ └── app.js
│ ├── lib
│ │ ├── font
│ │ ├── js
│ │ └── stylesheets
│ └── stylesheets
│ ├── style.css
│ └── style.less
├── routes
│ ├── github.js
│ └── index.js
└── views
├── index.hbs
├── orgs.hbs
└── partials
├── footer.hbs
└── header.hbs