原文: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