express 模版引擎 hbs

发布于 2021-08-09 22:45:42 字数 5101 浏览 1443 评论 0

前端模板选择标准

选择一门模版语言时,可能会考虑的几点

  • 语法友好(micro tmpl那种语法真是够了)
  • 支持模版嵌套(子模版的概念)
  • 支持模版继承(extend)
  • 前后端共用
  • 有容错处理(最好定位到具体出错位置)
  • 支持预编译(性能好)

注意到 hbs,似乎满足大部分的需求:https://github.com/donpark/hbs

getting started

demo地址:https://github.com/chyingp/blog/tree/master/demo/2015.04.01-hbs/getting-started
目录结构如下:

.
├── app.js
├── node_modules
├── package.json
└── views

看下app.js内容,还是比较容易理解的。模版views/index.hbs没什么好说的,语法跟handlbars一样

var express = require('express'),
  hbs = require('hbs'),
  app = express();

app.set('view engine', 'hbs');  // 用hbs作为模版引擎
app.set('views', __dirname + '/views'); // 模版所在路径

app.get('/', function(req, res){
  res.render('index', {title: 'hbs demo', author: 'chyingp'});
});

app.listen(3000);   

模版继承:layout.hbs

demo 地址:https://github.com/chyingp/blog/tree/master/demo/2015.04.01-hbs/inherit-from-layout

如果稍微看过hbs源码可以知道,hbs默认会到views下找layout.hbs这个模版,将这个模板作为基本骨架,来渲染返回的页面。

getting-started里的例子来说,比如用户请求 http://127.0.0.1:3000,那么,处理步骤如下

  1. 查找 views/index.hbs,进行编译,并将编译的结果保存为 A
  2. 查找 views/layout.hbs,如果
    1. 存在:对 layout.hbs进行编译,其中 {{{body}}} 标签替换成 A,并返回最终编译结果B
    2. 不存在:返回A

直接看例子。目录机构如下,可以看到多了个 layout.hbs

.
├── app.js
├── node_modules
│   ├── express
│   └── hbs
├── package.json
├── public
│   └── style.css
└── views
  ├── index.hbs
  ├── layout.hbs
  └── profile.hbs

layout.hbs的内容如下:

<!DOCTYPE html>
<html>
<head>
  <title>{{title}}</title>
  <link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>

{{{body}}}

</body>
</html>

相应的,index.hbs调整为

<h1>Demo by {{author}}</h1>

<p>{{author}}: welcome to homepage, I'm handsome!</p>

再次访问 http://127.0.0.1:3000,可以看到返回的页面

模版继承+自定义扩展

demo地址:https://github.com/chyingp/blog/tree/master/demo/2015.04.01-hbs/inherit-and-override

在项目中,我们会有这样的需求。页面的基础骨架是共享的,但某些信息,每个页面可能是不同的,比如引用的css文件、meta标签等。那么,除了上面提到的“继承”之外,还需要引入类似“覆盖”的特性。

hbs官方其实就提供了demo https://github.com/donpark/hbs/blob/master/examples/extend/ ,感兴趣的同学可以去围观下。可以看到,在app.js里面加入了下面的 helper function`,这就是实现”覆盖“ 的关键代码了。

var blocks = {};

hbs.registerHelper('extend', function(name, context) {
  var block = blocks[name];
  if (!block) {
    block = blocks[name] = [];
  }

  block.push(context.fn(this)); // for older versions of handlebars, use block.push(context(this));
});

hbs.registerHelper('block', function(name) {
  var val = (blocks[name] || []).join('\n');

  // clear the block
  blocks[name] = [];
  return val;
});

此外,layout.hbs需要做点小改动。里面比较明显的变化是加入了下面的block标记

{{{block "stylesheets"}}}
 {{{block "scripts"}}}

那么,可以在index.hbs里对这些标记的内容进行覆盖(或者说自定义),包括其他的模版,如果有需要,都可以对这两个`block进行覆盖。

{{#extend "stylesheets"}}
<link rel="stylesheet" href="/css/index.css"/>
{{/extend}}

let the magic begin

{{#extend "scripts"}}
<script>
  document.write('foo bar!');
</script>
{{/extend}}

那么问题来了。如果有这样的需求:所有的页面,都引用 style.css,只有 index.hbs 引用 index.css,那么上面的改动还不足以满足这个需求。

其实,只需要改几行代码就可以实现了,扩展性点个赞。改动后的app.js如下

var blocks = {};

hbs.registerHelper('extend', function(name, context) {
  var block = blocks[name];
  if (!block) {
    block = blocks[name] = [];
  }

  block.push(context.fn(this)); // for older versions of handlebars, use block.push(context(this));
});

// 改动主要在这个方法
hbs.registerHelper('block', function(name, context) {
  var len = (blocks[name] || []).length;
  var val = (blocks[name] || []).join('\n');

  // clear the block
  blocks[name] = [];

  return len ? val : context.fn(this);
});

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文