Handlebars一款js模版引擎,我们在做客户端开发的时候,也可能已经使用过。它语法比较简单,和我们平常写的html 一样,只不过html 中可以加入handlebars 表达式。 handlebars表达式用 {{variable}} 表示,当程序运行的时候,变量就被传进来的数据代替。下面就是一个比较简单的模版:

  1. <header>
  2. <h2>{{title}}</h2>
  3. <p>{{content}}</p>
  4. </header>

  当我们传递一个对象数据(如下)给这个模板的时候,

  1. <script>
  2. var context = {
  3. title:"My New Post",
  4. content: "This is first post"
  5. }
  6. </script>

  它就会被解析为

  1. <header>
  2. <h2>My New Post</h2>
  3. <p>This is first post</p>
  4. </header>

  非常简单,除了几个表达式,就是html 代码,当然,这也展示的它的特点,模板和数据分开,只定义视图,即需要展示给用户看的部分。

  Express 默认不支持Handlebars 模板,但我们可以通过 安装express-handlebars 第三方插件来进行支持,创建一个项目来体验一下 express和 handlebar.

  新建express 文件夹,npm init -y 快速建立 package.json 文件,安装 express 和 express-handlebars(npm install express express-handlebars -S). 再新建 server.js 作为项目的启动文件,views 文件夹作为 模版存放的地方,下面再建两个文件夹 layouts 和partials, 这两个文件夹的作用后面再说,先放这里。 目录如下:

  首先用express 搭建http 服务器(如下),在当前文件夹中调用cmd命令窗口,输入node server, 然后在浏览器地址栏中输入localhost:8080,  可以看到hello world ,表明服务器搭建成功

  1. var express = require('express'),
  2. app = express();
  3.  
  4. app.use(function (req,res) {
  5.  
  6. res.send("hello world")
  7. })
  8. app.listen(8080);

  由于 express 不支持Handlebars 模板,我们先要注册该模板引擎,调用app.engine() 方法来进行,express-handlebar 提供了两种方式来注册handlebars ,简单的看一下express-handlebars 里面 index.js 的源码

  1. var ExpressHandlebars = require('./lib/express-handlebars');
  2.  
  3. exports = module.exports = exphbs;
  4. exports.create = create;
  5. exports.ExpressHandlebars = ExpressHandlebars;
  6.  
  7. // -----------------------------------------------------------------------------
  8.  
  9. function exphbs(config) {
  10. return create(config).engine;
  11. }
  12.  
  13. function create(config) {
  14. return new ExpressHandlebars(config);
  15. }

  在项目中使用express-handlebars, 就需要引入它。 server.js 中 var exphbs =require('express-handlebars'); 引入它。当我们引入时,node 就会从node_module 中查找我们安装的模块,如果是一个文件夹,node 就会去查找该文件夹下的 index.js文件。我们看一下node_modules, express-handlebars 是一个文件夹,打开文件夹,可以看到index.js, 就是上面的源码,可以看到它暴露出了 exphbs 函数,我们的定义的变量exphbs 就是指向这个函数,它还可以接受一个配置参数。我们可以调用这个函数进行配置

  1. var express = require('express'),
  2. app = express();
  3.  
  4. // 引入express-handlebars
  5. var exphbs = require("express-handlebars")
  6. app.engine("handlebars", exphbs({
  7. defaultLayout: "main"
  8. }))
  9. app.listen(8080);

  app.engine 接受两个参数,一个是名字,这很好理解,注册肯定要给它起个名字,以便后面使用。第二个是函数,必须符合express 注册模版的规范,我们按照这个模式写,就可以了。它有好多配置选项,这只写了一个。

  我们看到它还有一个create 方法,我们也可以用这种方式进行配置

  1. app.engine('handlebars', exphbs.create({
  2. defaultLayout: "main",
  3. layoutDir: app.get('views') + '/layouts',
  4. partialsDir:[app.get('views') + '/partials']
  5. }).engine)

  上面这种写法,可能写的不太好理解,我们把它拆分一下, 又增加了两个配置项

  1. var hbs = exphbs.create({
  2. defaultLayout: "main",
  3. layoutDir: app.get('views') + '/layouts',
  4. partialsDir:[app.get('views') + '/partials']
  5. });
  6. app.engine('handlebars', hbs.engine);

  不管用哪种方法,我们都成功注册了handlebars模版,现在我们要告诉express来使用 handlerbars, 调用app.set 方法, 第二个参数是我们注册的模板的名字,就是app.engine中第一个参数,他们要保证一致

  1. app.set(“view engine”, 'handlebars' )

  最后还要告诉express 我们的模版文件放在什么地方,以便express 去查找使用,还是要调用app.set() 方法

  1. app.set("views", __dirname+ "/views"); // 第一个参数views是复数,一定不忘记后面的还有一个s, 要不然会报 View is not a construction 

  现在我们可以使用handlebar 模版引擎了,再来看一个注册模版擎时的参数 defaultLayout, layoutDir,partialsDir。default Layout, 默认布局, layout directory, 布局文件所在的目录,partial directory

局部文件所在的目录。 根据字面意思,我们也可以得到 defaultLayout: 设置默认布局文件,layoutDir 设置布局文件所在的位置,partialsDir 设置局部文件所在的位置。

  我们需要了解一下handlebars 中的基本概念 : 布局,局部, 还有视图。

  视图(views): 就是我们定义的任何的模板片段。 app.set("views", __dirname+ "/views"), 我们已经设置我们的视图文件所放的位置,就是views文件夹下。我们在于views 文件夹下定义一个 视图,index.handlebars

<div class="panel panel-primary">
  <div class="panel-heading">
    <h3 class="panel-title">欢迎使用handlebars</h3>
  </div>
</div>

  布局:也是一种模版,不过作用比较特殊,所以单独列出来。想一下我们的网站,主要分为header ,main, footer 三个部分,通常header ,footer 部分是不变的,只有main 是经常改变的, 程序在运行的过程中,只要动态的替换掉main 就可以了。这时我们就可以定义一个文件,包含不变的header, footer 和 可变的main,这个文件就是布局。在注册handlebars的时候,我们设置 defaultLayout 为main,其实是main.handlebars, 只不过是 省略了后缀名,并且设置 layoutDir( 布局文件所在的位置)为views文件夹下 layouts文件夹,那么就在 layouts文件夹新建main.handlebars 文件

  1. <!doctype>
  2. <html>
  3.   <head>
  4.     <title>Meadowlark Travel</title>
  5.     <link rel="stylesheet" href="/css/main.css">
  6. </head>
  7.   <body> {{{body}}} </body>
  8. </html>

  A layout is simply a Handlebars template with a {{{body}}} placeholder. Usually it will be an HTML page wrapper into which views will be rendered. 官网也说了,布局也是模版,只不过它 带有{{{body}}} 占位符,我们的view 就会渲染在{{{body}}}占位符所在的地方。

  局部文件(partial):有些时候,有几个页面要共用相同的部分,如侧边栏这,我们通常都会封装为组件,然而在handlebars 中,它们称之为局部文件partial,可以知道局部文件也是代码片段。我们在注册模版引擎的时候,设置 partialsDir(局部文件所在的路径)为views 下面的 partials文件夹,所以我们在partials文件夹下面建一个局部文件,命名为partial.handlebars

<div class="panel panel-default">
  <div class="panel-heading">
    <h3 class="panel-title">侧边栏</h3>
  </div>
</div>

  局部文件是几个页面要共用部分,它也应该包含在布局中。 在布局中,如何使用? {{> partial}} >加局部文件的名称,增加bootstrap

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Express Handlebars 使用</title>
  6. <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" >
  7. </head>
  8. <body>
  9. <div class="container">
  10. <div class="row">
  11. <div class="col-sm-8">
  12. {{{ body }}}
  13. </div>
  14. <div class="col-sm-4">
  15. {{> partial }}
  16. </div>
  17. </div>
  18. </div>
  19. </body>

   定义的视图,布局,局部文件,我们怎么使用呢?这要用到render方法,去渲染模版。render 方法是定义在res 响应对象上的,所以我们浏览器端发起一个请求,让它输出动态模版,所以我们在  server.js 中定义一个路由,

整个server.js如下

  1. var express = require('express'),
  2. app = express();
  3.  
  4. // 引入express-handlebars
  5. var exphbs = require("express-handlebars")
  6.  
  7. // 设置模板存放路径
  8. app.set("views", __dirname+ "/views")
  9.  
  10. // 注册handlebars模板引擎
  11. var hbs = exphbs.create({
  12. defaultLayout: "main",
  13. layoutDir: app.get('views') + '/layouts',
  14. partialsDir:[app.get('views') + '/partials']
  15. });
  16. app.engine('handlebars', hbs.engine);
  17.  
  18. // 告诉express使用handlebars模板,
  19. // 第二个参数是我们注册的模板的名字,就是app.engine中第一个参数,他们要保证一致
  20. app.set('view engine', 'handlebars' )
  21.  
  22. // render 渲染模板
  23. app.get('/', function (req,res) {
  24.  
  25. res.render('index')
  26. })
  27. app.listen(8080);

  render方法,接受一个参数,那就是要渲染的模版(视图)的名字,当exrpess 执行render 的时候,它会从views 文件夹下找到我们指定的文件,然后再找到布局文件,替换掉里面的{{{body}}},如果有partial, 它还会从particals 文件夹下找到局部文件,进行合并,形在一个完整的html文件进行输出。这也就是我们上面一系列设置默认文件夹的原因。

  我们每次渲染一个视图文件时,都会结合layout 布局模版渲染, 有时我们并不需要layout布局模版,这时可以在render 方法中进行设置 layout: false

  1. app.use(function (req,res) {
  2. res.render('404', {
  3. layout: false
  4. });
  5. })

  在设置模版引擎的时候,我们只指定了一个默认的布局视图,如果我们带想使用其他视图,怎么办? 调用render 方法的时候,指定layout,当然要确保这个layout文件在layout文件夹中。

  1. app.get('/foo', function(req, res){
  2. res.render('foo', { layout: 'microsite' });
  3. })

  现在我们页面都是静态的,没有任何改变。现在在index.handlbars 增加一个日期,{{time}}

  1. <div class="panel panel-primary">
  2. <div class="panel-heading">
  3. <h3 class="panel-title">
  4. <!-- 增加 time -->
  5. 欢迎使用handlebars模板 <small>{{timeFormate time}}<small>
  6. </h3>
  7. </div>
  8. </div>

  那么我们在渲染的时候要给time传递参数, render 方法可以接受第二个可选参数,它是一个对象, 就是我们向模版中传递的数据,对象的属性就是我们在模版中定义的表达式,如time

  1. res.render('index' , {
  2. time: Date.now()
  3. })

  这时我们发现,页面中显示的日期,但它是日期毫秒数,我们想把他转化成年月日的形式,这就要执行一定的逻辑操作,但是handlebars不支持在模版中使用逻辑的,这时要用到 helper 助手。它其实是一个函数,对模版中的表达式执行逻辑操作, helper 在模版中使用之前要先注册。这时有两种方法,

  一种是 在注册handlebars 模版引擎的时候,直接给它配置helpers

  1. var hbs = exphbs.create({
  2. defaultLayout: "main",
  3. layoutDir: app.get('views') + '/layouts',
  4. partialsDir:[app.get('views') + '/partials'],
  5. // 增加helpers
  6. helpers: {
  7. timeFormate: function (time) {
  8. var dateTime = new Date(time)
  9. return dateTime.getFullYear() +"年"+ (dateTime.getMonth()+1)+ "月" +
  10. dateTime.getDay() +'日'
  11. }
  12. }
  13. });

  一种是在调用render 方法的时候给它配置helpers

  1. app.get('/', function (req,res) {
  2.  
  3. res.render('index' , {
  4. time: Date.now(),
  5. //配置helpers
  6. helpers : {
  7. timeFormate: function (time) {
  8. var dateTime = new Date(time)
  9. return dateTime.getFullYear() +"年"+ (dateTime.getMonth()+1)+ "月" +
  10. dateTime.getDay() +'日'
  11. }
  12. }
  13. })
  14. })

  上面的第一种配置方式,可以叫做全局配置,因为它在所有的模版文件中都可以使用,下面的一种则只能在 index模版中使用,可以叫做局部配置。

  定义了helpers, 我们模版中就可以使用了,使用也简单,只要在模版的表达式前面加上就可以了。

  1. <div class="panel panel-primary">
  2. <div class="panel-heading">
  3. <h3 class="panel-title">
  4. <!-- 增加 timeFormate helper -->
  5. 欢迎使用handlebars模板 <small>{{timeFormate time}}<small>
  6. </h3>
  7. </div>
  8. </div>

  express-handlebars还支持子目录, 所以如果你有大量的局部文件,可以将它 们组织在一起。例如,你有一些社交媒体局部文件,可以将它们放在views/ partials/social 目录下面, 然后使用{{> social/facebook}}、{{> social/twitter}} 等来引入它们。


Express学习 ------模版引擎(handlebars)的更多相关文章

  1. js模版引擎handlebars.js实用教程

    js模版引擎handlebars.js实用教程 阅读本文需要了解基本的Handlebars.js概念,本文并不是Handlebars.js基础教程,而是注重于实际应用,为读者阐述使用过程中可能会遇到的 ...

  2. 模版引擎Handlebars和Mustache

    Handlebars是一款很高效的模版引擎,提供语意化的模版语句,最大的兼容Mustache模版引擎, 提供最大的Mustache模版引擎兼容, 无需学习新语法即可使用; 下面这个是基本的模版表达式, ...

  3. js模版引擎handlebars.js实用教程——为什么选择Handlebars.js

    返回目录 据小菜了解,对于java开发,涉及到页面展示时,比较主流的有两种解决方案: 1. struts2+vo+el表达式. 这种方式,重点不在于struts2,而是vo和el表达式,其基本思想是: ...

  4. js模版引擎handlebars.js实用教程——each-基本循环使用方法

    返回目录 <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content=" ...

  5. js模版引擎handlebars.js实用教程——each-循环中使用this

    返回目录 <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content=" ...

  6. js模版引擎handlebars.js实用教程——each嵌套

    <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content="text/ ...

  7. js模版引擎handlebars.js实用教程——循环中使用索引

    <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content="text/ ...

  8. js模版引擎handlebars.js实用教程——with-进入到某个属性(进入到某个上下文环境)

    返回目录 <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content=" ...

  9. js模版引擎handlebars.js实用教程——with-终极this应用

    返回目录 <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content=" ...

随机推荐

  1. 线程安全之CAS机制详解(分析详细,通俗易懂)

    背景介绍:假设现在有一个线程共享的变量c=0,让两个线程分别对c进行c++操作100次,那么我们最后得到的结果是200吗? 1.在线程不安全的方式下:结果可能小于200,比如当前线程A取得c的值为3, ...

  2. keystone系列二:HTTP协议

    一 为何要学习HTTP协议 http协议就是通信的双方共同遵守的标准,就好比要合伙办事的两家公司签署的合同. openstack中各组件是基于restful api通信的,restful api可以单 ...

  3. 给大家推荐8个SpringBoot精选项目

    前言 2017年,曾在自己的博客中写下这样一段话:有一种力量无人能抵挡,它永不言败生来倔强.有一种理想照亮了迷茫,在那写满荣耀的地方. 如今2018年已过大半,虽然没有大理想抱负,但是却有自己的小计划 ...

  4. 探讨.NET Core中实现AES加密和解密以及.NET Core为我们提供了什么方便!

    前言 对于数据加密和解密每次我都是从网上拷贝一份,无需有太多了解,由于在.net core中对加密和解密目前全部是统一了接口,只是做具体的实现,由于遇到过问题,所以将打算基本了解下其原理,知其然足矣, ...

  5. 图解HTTP,TCP,IP,MAC的关系

    入门 用户发了一个HTTP的请求,想要访问我们网站的首页,这个HTTP请求被放在一个TCP报文中,再被放到一个IP数据报中,最终的目的地就是我们的115.39.19.22. 进阶 IP数据报其实是通过 ...

  6. ML.NET 示例:推荐之场感知分解机

    写在前面 准备近期将微软的machinelearning-samples翻译成中文,水平有限,如有错漏,请大家多多指正. 如果有朋友对此感兴趣,可以加入我:https://github.com/fei ...

  7. Codeforces Round #486 (Div. 3)-B. Substrings Sort

    B. Substrings Sort time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  8. Python入门-三级菜单

    作业题目: 三级菜单 作业需求: menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村':{ '爱奇艺':{}, ...

  9. [2017BUAA软工助教]团队alpha得分总表

    一.累计得分 项目 介绍 采访 贡献分 功能 技术 α例会 α发布 α测试 α展示 α事后 合计 满分 10 10 10 10 10 50 10 10 150 10 280 hotcode5 10 9 ...

  10. PAT L3-010 是否完全二叉搜索树

    https://pintia.cn/problem-sets/994805046380707840/problems/994805049870368768 将一系列给定数字顺序插入一个初始为空的二叉搜 ...