hbs是Express提供的默認視圖引擎, 是對Handlerbars的封裝. 具體用法參考下面的項目地址, 這里額外說明使用Handlerbars模板引擎及其開發(fā)配套的一些插件用法.
Handlerbars使用環(huán)境: Node+Express+hbs后端渲染模式
主插件
拓展插件
Handlerbars
Handlebars是一個Javascript模板引擎, 能讓你輕松高效的編寫語義化模板, 它是Mustache模板引擎的一個擴展, Handlebars和Mustache都是弱邏輯的模板引擎, 能將Web前端的視圖和代碼分離, 降低兩者之間耦合.
Handlebars以聲明式的書寫方式定義模板邏輯, 一切都是表達式, 編寫簡單易于拓展, 可前后端共用.
學習Handlebars主要是理解:
- 模板函數(shù): Markup字符串 = 模板函數(shù) + 數(shù)據(jù)
- Helper: 邏輯處理/數(shù)據(jù)過濾/內(nèi)容轉(zhuǎn)移等, 使用前必須注冊
- Partials: 子模板, 使用前必須注冊
-
{{}}
和{{{}}}
的區(qū)別 - inline helper和block helper的不同寫法
hbs
Express.js view engine for handlebars.js
hbs
是一個運行在Express上, 對Handlerbars
模板引擎再次封裝的視圖引擎.
特點
1. registerPartials
registerPartial
可輸入路徑注冊, 對應的方法: hbs.registerPartials
, 會比原始方法更便捷.
2. localsAsTemplateData
可在視圖模板中傳入node環(huán)境變量/全局變量
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. 如何根據(jù)頁面插入對應的style
和script
可以使用下面講到的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四種子模板嵌套結構, 具體來說就是: 繼承/嵌套/定義內(nèi)容及插入方式/插入點.
這四種功能我認為已經(jīng)能覆蓋到所有使用的環(huán)境了.
Helper介紹
1. extend
Extend Helper是繼承的意思, 與ES6的Class Extend類似, 將繼承的模板拿來與當前模板整合, Extend的內(nèi)容不能包括HTML的tag, 只能是各類Helper.
邏輯思路是這樣:
將"layout"子模板拿來, 嵌入
content
中定義的內(nèi)容(根據(jù)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的內(nèi)容不能包括HTML的tag, 只能是各類Helper.
邏輯思路是這樣:
將"gallery"子模板拿來,"gallery"中嵌入內(nèi)部定義的
content
內(nèi)容, 之后將"gallery"整體嵌入當前模板內(nèi), 之后是繼承的操作...
例如:
{{#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定義一個插入點, 插入的內(nèi)容由Content Helper定義. Block Helper內(nèi)部可定義一些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定義一個插入內(nèi)容, 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中調(diào)用handlebars的原始實例(不需要重復引入handlebars
)
hbs.registerHelper(layouts(hbs.handlebars))
2. 子模板未找到
extend
和embed
操作的模板都是partials
, 注意使用前需要registerPartials
注冊.
3. hbs子模板更改頁面沒生效
registerPartials
注冊是一次性行為, 除非有watch文件再次執(zhí)行注冊, 或者重啟node服務, 否則node中保存的都是第一次的編譯結果, 關于watch的工具會在hbs-utils
中講到.
4.extend
和embed
書寫的partials在頁面沒正常初始化
可能是未正常初始化的Partials使用了別的模塊注冊的Helper, 且這個Helper沒有在handlebars-layouts
之前先注冊, 更改下順序吧, 比如handlebars-helpers
和handlebars-layouts
的順序:
1. handlebars-helpers: 提供基礎的Helper
2. handlebars-layouts: 提供布局的Helper
handlebars-helpers
這個是各類Handlerbars的Helper集合, 涵蓋了全部可能用到的Helper, 不需要自己再實現(xiàn)一遍. 具體內(nèi)容參考這里.
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
注冊的, 需要使用hbs
的registerHelper
的方法完成注冊, 但是插件默認是使用handlebars
, 因此需要手動傳入hbs
對象:
var helpers = require('handlebars-helpers')
helpers({handlebars: hbs})
可以通過下面的方法查看是否成功注冊了Helper:
console.log(Object.keys(hbs.handlebars.helpers))
hbs-utils
這個工具是在開發(fā)時為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
之間的區(qū)別
主要是提供了precompile
的功能, 默認是關閉的的. 另外, 提供name
屬性來修改Partials
的注冊名稱.
2. registerWatchedPartials
之前需要registerPartials
嗎?
不需要, 因為registerWatchedPartials
會自己按照給定的目錄先注冊Partials
, 之后再watch.
3. 如何開啟開發(fā)模式
提前設好模式, 根據(jù)下面的判斷開啟
if (process.env.NODE_ENV === 'development') {
...
} else {
...
}
總結
以上是我在使用hbs時的最佳時間和插件組合, 希望能對你有用.