hbs使用最佳實踐

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ù)頁面插入對應的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四種子模板嵌套結構, 具體來說就是: 繼承/嵌套/定義內(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. 子模板未找到

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

3. hbs子模板更改頁面沒生效

registerPartials注冊是一次性行為, 除非有watch文件再次執(zhí)行注冊, 或者重啟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, 不需要自己再實現(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注冊的, 需要使用hbsregisterHelper的方法完成注冊, 但是插件默認是使用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時的最佳時間和插件組合, 希望能對你有用.

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市酒朵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扎附,老刑警劉巖蔫耽,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異留夜,居然都是意外死亡匙铡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門碍粥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慰枕,“玉大人,你說我怎么就攤上這事即纲。” “怎么了博肋?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵低斋,是天一觀的道長蜂厅。 經(jīng)常有香客問我,道長膊畴,這世上最難降的妖魔是什么掘猿? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮唇跨,結果婚禮上稠通,老公的妹妹穿的比我還像新娘。我一直安慰自己买猖,他們只是感情好改橘,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著玉控,像睡著了一般飞主。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上高诺,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天碌识,我揣著相機與錄音,去河邊找鬼虱而。 笑死筏餐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的牡拇。 我是一名探鬼主播魁瞪,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼诅迷!你這毒婦竟也來了佩番?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤罢杉,失蹤者是張志新(化名)和其女友劉穎趟畏,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滩租,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡赋秀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了律想。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猎莲。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖技即,靈堂內(nèi)的尸體忽然破棺而出著洼,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布身笤,位于F島的核電站豹悬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏液荸。R本人自食惡果不足惜瞻佛,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娇钱。 院中可真熱鬧伤柄,春花似錦、人聲如沸文搂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽细疚。三九已至蔗彤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疯兼,已是汗流浹背然遏。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吧彪,地道東北人待侵。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像姨裸,于是被迫代替她去往敵國和親秧倾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容