Handlebars partials 隐藏的力量
一個項目的機會再加上我自己的探索,讓我對Handlebars partials有了更深的理解。事實證明,你可以做得比我了解的更多。
我最近在負責一個小項目,只有很少的靜態(tài)頁面。因為太小了,我們最開始沒有使用模板系統(tǒng)。當項目開始往深一層推進時,我們發(fā)現(xiàn)把靜態(tài)頁面拆分成partials & layout的模板明顯更好。
我們以前用過handlebars-layouts并且很喜歡它提供的那些特性。我本來想同時安裝Handlebars和handlebars-layouts,但是又感覺過于累贅了。
我在想有沒有可能只使用Handlebars而不用額外的handlebars-layouts庫?
我并不是想吐槽handlebars-layouts,我們以后可能還會用到這個項目。但是我想試試只用Handlebars partials來實現(xiàn)一些handlebars-layouts通過helper來實現(xiàn)的一些特性。
花了一些時間來評估項目需求后我問自己:handlebars-layouts helpers提供的哪些特性是我需要的?根據(jù)過去的經(jīng)驗,我列出了如下helpers:
- {{#extend}} layouts的能力
- {{#embed}} partials的能力
- 能像{{#block}} 和 {{#content}}一樣配合
列出來后,我開始查Handlebars partials文檔來看看行不行。然后我發(fā)現(xiàn)我從來沒有利用到partials的全部潛力,只要做一點小小的努力就可以讓它們實現(xiàn)我的需求。有兩個特性吸引了我:partial blocks和inline partials。
我興奮地意識到Handlebars partials可以做到比我想象中多得多的事情并馬上開始了工作。
Partials基礎(chǔ)
在開始前,還是先看一看基本partials, partial blocks 和 inline partials。
基本partials就像這樣:
{{> content-block }}它會試圖找到一個命名為content-block的partial進行渲染,否則就報錯。
Handlebars partial的文檔上寫:
The normal behavior when attempting to render a partial that is not found is for the implementation to throw an error. If failover is desired instead, partials may be called using the block syntax.渲染一個partial的默認行為是,如果沒找到就報錯。如果要進行錯誤處理,就要用block的方式來使用。
partial block就像這樣:
{{#> content-block}}Default content {{/content-block}}這樣寫有一個好處,就是當content-block沒有找到時,partial block里面的部分就會被渲染,像上面就會渲染“Default content”。
而inline partial就像這樣:
{{#*inline "content-block"}}My new content {{/inline}}文檔里是這樣說的:
Templates may define block scoped partials via the inline decorator.通過使用inline修飾符,可以在模板中定義塊級partials。
inline partial允許你臨時創(chuàng)建partials。如果頁面中有一個content-block partial block,然后我們創(chuàng)建了content-block inline partial的話,inline partial中的內(nèi)容(“My new content”) 就會替換partial block中默認內(nèi)容。inline partial同樣可以替換基本partials,只是基本partials沒有默認內(nèi)容而已。
了解完這些后我們就可以開始了。
目錄結(jié)構(gòu)
我想同時擁有l(wèi)ayouts/、includes/和pages/,目錄結(jié)構(gòu)看起來如下:
src/ ├── pages │ ├── page-one.hbs │ └── page-two.hbs └── partials├── includes│ ├── hero.hbs│ └── footer.hbs└── layouts└── base.hbs為了進行渲染使用了Gulp + gulp-compile-handlebars,在htmlgulp任務(wù)中把src/pages/*.hbs當成source目錄,就像下面這樣:
// gulpfile.jsconst handlebars = require('gulp-compile-handlebars'); const rename = require('gulp-rename');gulp.task('html', () => {return gulp.src('./src/pages/*.hbs').pipe(handlebars({}, {ignorePartials: true,batch: ['./src/partials']})).pipe(rename({extname: '.html'})).pipe(gulp.dest('./dist')); });需要注意,我們口中的“page”, “include”和“l(fā)ayout”,本質(zhì)上都是Handlebars partial,這就是保證可擴展性的關(guān)鍵。一旦了解了這個,新世界的大門就打開了。
Layouts, pages 和 includes
再來分別看看每個文件:
Layouts 文件
首先是layouts/base.hbs:
{{!-- layouts/base.hbs --}}<!doctype html> <html><head><title>{{#if title}}{{title}}{{else}}Base Page Title{{/if}}</title><link rel="stylesheet" href="main.css">{{#> head-block}}{{!-- Custom <head> content per page could be added. --}}{{/head-block}}</head><body>{{#> hero-block}}{{!-- Hero content goes here. --}}{{/hero-block}}<footer>{{#> footer-block}}{{!--The `includes/footer` partial is the default content,but can be overridden.--}}{{> includes/footer }}{{/footer-block}}</footer>{{#> scripts-block}}{{!-- Custom scripts per page can be added. --}}{{/scripts-block}}</body> </html>有些值得注意的地方,拆開來單獨看:
<link rel="stylesheet" href="main.css"> {{#> head-block}}{{!-- Custom <head> content per page could be added. --}} {{/head-block}}引入了一個虛構(gòu)的main.css。然后設(shè)置了head-block,當一個具體的page繼承此layout時在這里傳入<head>中的內(nèi)容(注:就跟handlebars-layouts的{{#block}} helper一樣)。
{{#> hero-block}}{{!-- Hero content goes here. --}} {{/hero-block}} {{#> scripts-block}}{{!-- Custom scripts per page can be added. --}} {{/scripts-block}}跟head-block一樣,我們在hero和scripts部分都用了Handlebars partial blocks。在同一套模板有不同的內(nèi)容和scripts的時候,顯得更加靈活。
<footer>{{#> footer-block}}{{!--The `includes/footer` partial is the default content,but can be overridden.--}}{{> includes/footer }}{{/footer-block}} </footer>footer部分我們又用了Handlebars partial block,不同的是我們用了{{> includes/footer }}基本partial來指定默認內(nèi)容。
當footer-block沒有被傳入內(nèi)容時就會渲染默認內(nèi)容。所有代碼中,我們都用的Handlebars注釋(注:這些注釋不會被渲染到HTML中,如果你用HTML注釋就會被渲染到HTML中,因為partial block中所有內(nèi)容都會被渲染)。
page 文件
拼圖的下一部分就是page partial。我們也用到了Handlebars partial blocks,同時還有Handlebars inline partials,下面是pages/page-one.hbs的示例:
{{!-- pages/page-one.hbs --}}{{#> layouts/base title="Page One" }}{{#*inline "hero-block"}}{{> includes/herohero-src="img/hero-1.png"hero-alt="Hero 1 alt title"}}{{/inline}}{{/layouts/base}}同樣拆開來看看:
{{#> layouts/base title="Page One" }}... {{/layouts/base}}這里又用到了Handlebars partial block。但是這次,我們用它來繼承l(wèi)ayouts/base(注:就跟handlebars-layouts {{#extend}} helper一樣),同時設(shè)置了page的title(注:用到了partial 參數(shù)特性)。
{{#*inline "hero-block"}}... {{/inline}}這是我們第一次用到Handlebars inline partial。這個inline partial被傳入layouts/base然后被其中的hero-block注入(注:用法就跟 handlebars-layouts 的 {{#content}} helper 一樣)。
{{> includes/herohero-src="http://fpoimg.com/500x200"hero-alt="Hero 1 alt title" }}最后我們引入indludes/hero基本partial(注:就跟handlebars-layouts {{#embed}} helper一樣)。
includes 文件
includes/*.hbs可以被layouts 和 pages引用。既然都用到了,那就看看大概是什么樣子的:
{{!-- includes/hero.hbs --}}<div class="hero"><img src="{{hero-src}}" alt="{{hero-alt}}"/> </div>沒什么開創(chuàng)性的東西,只是簡單地渲染傳入的hero-src 和 hero-alt(注:可以改進的地方:用{{#if}}{{else}}來判斷參數(shù)是否為空)。
再看看includes/footer.hbs:
{{!-- includes/footer.hbs --}}<p>This is some default footer content.</p>沒啥特別的,這就是layouts/base中footer的默認內(nèi)容。
最后成果
來概括一下所有東西。
layouts/base.hbs
- 充當基礎(chǔ)layout文件
- 使用partial blocks定義默認和動態(tài)內(nèi)容
pages/page-one.hbs
- 充當page文件
- 使用partial block來繼承基本layout
- 使用inline partials填充內(nèi)容到layout中的 partial blocks
includes/*.hbs
- partials可以被 layouts 或 pages 引用
- 可以在partial block或inline partial中使用
然后就是最后渲染出的page-one.html文件,看起來就是這樣:
<!-- page-one.html --><!doctype html> <html><head><title>Page One</title><!-- No extra `head-block` content was passed in, only `main.css` from the base layout rendered.--><link rel="stylesheet" href="main.css"></head><body><!-- The `hero-block` content --><div class="hero"><img src="http://fpoimg.com/500x200" alt="Hero 1 alt title"/></div><footer><!-- The `footer-block` content --><p>This is some default footer content.</p></footer></body> </html>讓我們再試試使用同一個layout,但改一些東西,就叫它pages/page-two.hbs好了:
{{!-- pages/page-two.hbs --}}{{#> layouts/base title="Page Two" }}{{!-- Let's add a second stylesheet for this layout. --}}{{#*inline "head-block"}}<link rel="stylesheet" href="some-other-styles.css">{{/inline}}{{!-- Let's change the hero image for this layout. --}}{{#*inline "hero-block"}}{{> includes/herohero-src="http://fpoimg.com/400x400"hero-alt="Hero 2 alt title"}}{{/inline}}{{!-- Let's override the "footer-block" content. --}}{{#*inline "footer-block"}}<p>We are now overriding the default "footer-block" content with this content.</p>{{/inline}}{{!-- Let's add a script for this layout. --}}{{#*inline "scripts-block"}}<script src="new-script.js"></script>{{/inline}}{{/layouts/base}}渲染出來就是這樣:
<!-- page-two.html --><!doctype html> <html><head><title>Page Two</title><link rel="stylesheet" href="main.css"><!-- The `head-block` added a stylesheet. --><link rel="stylesheet" href="some-other-styles.css"></head><body><!-- Our new `hero-block` content. --><div class="hero"><img src="http://fpoimg.com/400x400" alt="Hero 2 alt title"/></div><footer><!-- We overrode the default `footer-block` content. --><p>We are now overriding the default "footer-block" content with this content.</p></footer><!-- The `script-block` added a script. --><script src="new-script.js"></script></body> </html>這樣我們就用同一個layout渲染出了兩個不同的頁面。
感謝
我們用Handlebars 的partial blocks, inline partials, basic partials 和partial parameters模擬出了handlebars-layouts的{{#extend}, {{#embed}}, {{#block}} 和 {{#content}} helpers。
這是一次有趣的嘗試,讓我更好地理解了Handlebars partials。注意在不同的項目中,我們必須先評估庫提供的特性,有哪些是需要的,哪些是不需要的,沒有一個完美的解決方案(注:有些特性是無法模擬的,比如 {{#content}} 的 append 和 prepend,又比如content作subexpression時,用conditonal blocks 檢查content是否為空)。
如果你想要了解更多,可以看看這個項目,試試各種組合,看看能不能挖掘出更多的Handlebars partials的潛力。
總結(jié)
以上是生活随笔為你收集整理的Handlebars partials 隐藏的力量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 博世应对汽车变革的“十八般兵器”
- 下一篇: 登录功能测试点