hbs使用最佳实践

hbs是Express提供的默认视图引擎, 是对Handlerbars的封装. 具体用法参考下面的项目地址, 这里额外说明使用Handlerbars模板引擎及其开发配套的一些插件用法.

Handlerbars使用环境: Node+Express+hbs后端渲染模式

主插件

拓展插件

Handlerbars

Handlebars是一个Javascript模板引擎, 能让你轻松高效的编写语义化模板, 它是Mustache模板引擎的一个扩展, Handlebars和Mustache都是弱逻辑的模板引擎, 能将Web前端的视图和代码分离, 降低两者之间耦合.

Handlebars以声明式的书写方式定义模板逻辑, 一切都是表达式, 编写简单易于拓展, 可前后端共用.

学习Handlebars主要是理解:

  • 模板函数: Markup字符串 = 模板函数 + 数据
  • Helper: 逻辑处理/数据过滤/内容转移等, 使用前必须注册
  • Partials: 子模板, 使用前必须注册
  • {{}}{{{}}}的区别
  • inline helper和block helper的不同写法

hbs

Express.js view engine for handlebars.js

hbs是一个运行在Express上, 对Handlerbars模板引擎再次封装的视图引擎.

特点

1. registerPartials

registerPartial可输入路径注册, 对应的方法: hbs.registerPartials, 会比原始方法更便捷.

2. localsAsTemplateData

可在视图模板中传入node环境变量/全局变量

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

var app = express();
hbs.localsAsTemplateData(app);

app.locals.foo = "bar";
top level: {{@foo}}

3. handlebars实例

因为是对Handlerbars的封装, Handlerbars的实例在这里取到:

// hbs.handlebars is the handlebars module
hbs.handlebars === require('handlebars');

问题点

1. 如何根据页面插入对应的stylescript

可以使用下面讲到的handlebars-layouts处理这个需求, 主要是使用Helper的特性.

2. 如何更换layout.hbs的路径和名称

// view的路径
app.set('views', path.join(__dirname, 'client/views'))
// 模板后缀
app.set('view engine', 'hbs')
// layout名称
app.set('view options', {layout: 'layout.hbs'})

3. 模板加载顺序

1. compile body template(inject all partials and helpers) 
2. inject to layout template

handlebars-layouts

这个插件提供handlerbars的基础布局的helper, 包括: extend/embed/content/block四种子模板嵌套结构, 具体来说就是: 继承/嵌套/定义内容及插入方式/插入点.

这四种功能我认为已经能覆盖到所有使用的环境了.

Helper介绍

1. extend

Extend Helper是继承的意思, 与ES6的Class Extend类似, 将继承的模板拿来与当前模板整合, Extend的内容不能包括HTML的tag, 只能是各类Helper.

逻辑思路是这样:

将"layout"子模板拿来, 嵌入content中定义的内容(根据content的名称在"layout"中找到对应的block), 之后返回组装好的模板.

例如:

{{#extend "layout"}}
    {{#content "head" mode="append"}}
        <link rel="stylesheet" href="assets/css/home.css" />
    {{/content}}

    {{#content "body"}}
        <h2>Welcome Home</h2>

        <ul>
            {{#items}}
                <li>{{.}}</li>
            {{/items}}
        </ul>
    {{/content}}

    {{#content "foot" mode="prepend"}}
        <script src="assets/js/analytics.js"></script>
    {{/content}}
{{/extend}}

2. embed

Embed Helper是嵌入的意思, 将Embed模板嵌入当前的子模板中, 同样, Embed的内容不能包括HTML的tag, 只能是各类Helper.

逻辑思路是这样:

将"gallery"子模板拿来,"gallery"中嵌入内部定义的content内容, 之后将"gallery"整体嵌入当前模板内, 之后是继承的操作...

例如:

{{#extend "layout"}}

    {{#content "body"}}
        {{#embed "gallery"}}
            {{#content "body"}}
                ![](1.png)
                ![](2.png)
            {{/content}}
        {{/embed}}

        {{#embed "modal" foo="bar" name=user.fullName}}
            {{#content "title" mode="prepend"}}Image 1 - {{/content}}
            {{#content "body"}}![](1.png){{/content}}
        {{/embed}}
    {{/content}}

{{/extend}}

3. block

Block Helper定义一个插入点, 插入的内容由Content Helper定义. Block Helper内部可定义一些HTML Markup.

逻辑思路是这样:

类似于Vue/Angular的slot概念

例如:

{{#block "header"}}
    <h1>Hello World</h1>
{{/block}}

{{#block "main"}}
    <p>Lorem ipsum...</p>
{{/block}}

{{#block "footer"}}
    <p>© 1970</p>
{{/block}}

4. content

Content Helper定义一个插入内容, mode可以决定插入的方式, 比如: 前插入(prepend)/后插入(append)/替换(replace). 默认是替换(replace).

例如:

{{#extend "layout"}}

    {{#content "header"}}
        <h1>Goodnight Moon</h1>
    {{/content}}

    {{#content "main" mode="append"}}
        <p>Dolor sit amet.</p>
    {{/content}}

    {{#content "footer" mode="prepend"}}
        <p>MIT License</p>
    {{/content}}

{{/extend}}

安装

var hbs = require('hbs')
var layouts = require('handlebars-layouts')
hbs.registerHelper(layouts(hbs.handlebars))

问题点

1. createFrame未定义报错

hbs是对handlebars的再次封装, 因此handlebars-layouts初始化使用的handlebars实例并不是hbs, 因为hbs中并没有createFrame方法(handlebars-layouts需要这个方法, 没有会报错).

因此, 初始化时需要从hbs中调用handlebars的原始实例(不需要重复引入handlebars)

hbs.registerHelper(layouts(hbs.handlebars))

2. 子模板未找到

extendembed操作的模板都是partials, 注意使用前需要registerPartials注册.

3. hbs子模板更改页面没生效

registerPartials注册是一次性行为, 除非有watch文件再次执行注册, 或者重启node服务, 否则node中保存的都是第一次的编译结果, 关于watch的工具会在hbs-utils中讲到.

4.extendembed书写的partials在页面没正常初始化

可能是未正常初始化的Partials使用了别的模块注册的Helper, 且这个Helper没有在handlebars-layouts之前先注册, 更改下顺序吧, 比如handlebars-helpershandlebars-layouts的顺序:

1. handlebars-helpers: 提供基础的Helper
2. handlebars-layouts: 提供布局的Helper

handlebars-helpers

这个是各类Handlerbars的Helper集合, 涵盖了全部可能用到的Helper, 不需要自己再实现一遍. 具体内容参考这里.

More than 130 Handlebars helpers in ~20 categories.

安装

var hbs = require('hbs')
var helpers = require('handlebars-helpers')
helpers({handlebars: hbs})

问题点

1. 按照handlebars-helpers文档安装, Helper未成功注册

因为这个插件是自动做registerHelper注册的, 需要使用hbsregisterHelper的方法完成注册, 但是插件默认是使用handlebars, 因此需要手动传入hbs对象:

var helpers = require('handlebars-helpers')
helpers({handlebars: hbs})

可以通过下面的方法查看是否成功注册了Helper:

console.log(Object.keys(hbs.handlebars.helpers))

hbs-utils

这个工具是在开发时为hbs提供Partials注册及watch的功能.

安装

var hbs = require('hbs')
var hbsutils = require('hbs-utils')(hbs)
hbsutils.registerWatchedPartials(config.viewsPath, {
    onchange () {
      // Partials has changed!
      console.log(`Partials has changed!`)
    }
  }, function () {
    // The initial registration of partials is complete.
    console.log(`The initial registration of partials is complete`)
})

问题点

1. 和hbs提供的registerPartials之间的区别

主要是提供了precompile的功能, 默认是关闭的的. 另外, 提供name属性来修改Partials的注册名称.

2. registerWatchedPartials之前需要registerPartials吗?

不需要, 因为registerWatchedPartials会自己按照给定的目录先注册Partials, 之后再watch.

3. 如何开启开发模式

提前设好模式, 根据下面的判断开启

if (process.env.NODE_ENV === 'development') {
    ...
} else {
    ...
}

总结

以上是我在使用hbs时的最佳时间和插件组合, 希望能对你有用.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容