一个项目的机会再加上我自己的探索,让我对Handlebars partials有了更深的理解。事实证明,你可以做得比我了解的更多。

我最近在负责一个小项目,只有很少的静态页面。因为太小了,我们最开始没有使用模板系统。当项目开始往深一层推进时,我们发现把静态页面拆分成partials & layout的模板明显更好。

我们以前用过handlebars-layouts并且很喜欢它提供的那些特性。我本来想同时安装Handlebars和handlebars-layouts,但是又感觉过于累赘了。

我在想有没有可能只使用Handlebars而不用额外的handlebars-layouts库?

我并不是想吐槽handlebars-layouts,我们以后可能还会用到这个项目。但是我想试试只用Handlebars partials来实现一些handlebars-layouts通过helper来实现的一些特性。

花了一些时间来评估项目需求后我问自己:handlebars-layouts helpers提供的哪些特性是我需要的?根据过去的经验,我列出了如下helpers:

  • {{#extend}} layouts的能力
  • {{#embed}} partials的能力
  • 能像{{#block}} 和 {{#content}}一样配合

列出来后,我开始查Handlebars partials文档来看看行不行。然后我发现我从来没有利用到partials的全部潜力,只要做一点小小的努力就可以让它们实现我的需求。有两个特性吸引了我:partial blocks和inline partials。

我兴奋地意识到Handlebars partials可以做到比我想象中多得多的事情并马上开始了工作。

Partials基础

在开始前,还是先看一看基本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允许你临时创建partials。如果页面中有一个content-block partial block,然后我们创建了content-block inline partial的话,inline partial中的内容(“My new content”) 就会替换partial block中默认内容。inline partial同样可以替换基本partials,只是基本partials没有默认内容而已。

了解完这些后我们就可以开始了。

目录结构

我想同时拥有layouts/includes/pages/,目录结构看起来如下:

src/
├── pages
│   ├── page-one.hbs
│   └── page-two.hbs
└── partials├── includes│   ├── hero.hbs│   └── footer.hbs└── layouts└── base.hbs

为了进行渲染使用了Gulp + gulp-compile-handlebars,在htmlgulp任务中把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”和“layout”,本质上都是Handlebars partial,这就是保证可扩展性的关键。一旦了解了这个,新世界的大门就打开了。

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}}

引入了一个虚构的main.css。然后设置了head-block,当一个具体的page继承此layout时在这里传入<head>中的内容(注:就跟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。在同一套模板有不同的内容和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来指定默认内容。

footer-block没有被传入内容时就会渲染默认内容。所有代码中,我们都用的Handlebars注释(注:这些注释不会被渲染到HTML中,如果你用HTML注释就会被渲染到HTML中,因为partial block中所有内容都会被渲染)。

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。但是这次,我们用它来继承layouts/base(注:就跟handlebars-layouts {{#extend}} helper一样),同时设置了page的title(注:用到了partial 参数特性)。

{{#*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>

没什么开创性的东西,只是简单地渲染传入的hero-srchero-alt(注:可以改进的地方:用{{#if}}{{else}}来判断参数是否为空)。

再看看includes/footer.hbs

{{!-- includes/footer.hbs --}}<p>This is some default footer content.</p>

没啥特别的,这就是layouts/base中footer的默认内容。

最后成果

来概括一下所有东西。

layouts/base.hbs

  • 充当基础layout文件
  • 使用partial blocks定义默认和动态内容

pages/page-one.hbs

  • 充当page文件
  • 使用partial block来继承基本layout
  • 使用inline partials填充内容到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的潜力。

Handlebars partials 隐藏的力量相关推荐

  1. handlebars.php,Handlebars partials 隐藏的力量

    一个项目的机会再加上我自己的探索,让我对Handlebars partials有了更深的理解.事实证明,你可以做得比我了解的更多. 我最近在负责一个小项目,只有很少的静态页面.因为太小了,我们最开始没 ...

  2. 程序猿生存定律--表达背后的力量(1)

    程序猿生存定律这系列的文件夹在这里:程序猿生存定律--文件夹 喜欢从头瞄的,能够移步. -------------------------------------------------------- ...

  3. 程序员生存定律[七] 表达背后的力量

    前面讲的主要是提升一个人自身的价值,对于程序员而言,自身价值几乎与技术能力相等价.对喜欢编程的程序员而言,最简单的生存方式也许是最幸福的,比如说:我只把精力放在提高编程技术上,并且工作中也只负责把程序 ...

  4. ◆[转]瓶子生日密码之2月2日

    2月2日 品味高尚 2月2日出生的人不具有洗练.优雅.高中的气质,而且可以具体的散了出他们所处的社会族群,或国籍的许多典型物质,包括最好与最差的.因此,不管他们是多么的特立独行,他们还是紧密地依附在自 ...

  5. airpods2突然变得很小声_11岁女孩胸部发育被同学取笑,穿束胸衣上课突然晕倒...

    各位读者大家好,感谢大家在百忙之中点开小编的文章,每次你们的阅读和评论都是对于小编的认可.喜欢的话就先点击上面的蓝色字体  "温州身边事",再点击"关注",就当 ...

  6. 新时代动画电影的互联网宣传

    新时代动画电影的互联网宣传 --动画电影<大圣归来>互联网营销六阶段解析 摘要:对<大圣归来>营销传播过程稍加留意便会发现,这是一个"断言.重复.传染"的社 ...

  7. 纪念爱因斯坦提出相对论100周年,推荐好文!

    注:虽然我的第一次"相对论之旅" 以失败告终,但是在我不离不弃的追求后,对相对论的认识又更近了一步. 这篇文章写得非常好,对帮助理解相对论不无裨益,推荐阅读. 最后再次向爱因斯坦这 ...

  8. 许三多:浮躁社会的反义词

    浮躁,是对这个社会最好的写照,在这浮躁的社会里,只要你能够抵挡住它,你将过的很快乐,还有一个词来形容它:那就是"钝感"!   转贴自:http://news.sina.com.cn ...

  9. 阻容感基础08:电感特性原理(2)- 电磁感应定律

    说在开头:关于宇宙常数 大家对"万有引力"都有切身和直观的感受,地球的重力以及日月星辰的圈圈运动等.但是相对于其它3种作用力:强力.弱力甚至电磁力来说就显得太弱小了.电磁力比引力要 ...

最新文章

  1. dac生成信号频率取决于_DAC和AOC,谁将是数据通信领域最终赢家?
  2. android调用h5预加载图片,使用HTML5的页面资源预加载(Link prefetch)功能加速你的页面加载速度...
  3. Error creating bean with name 'org.springframework.amqp.rabbit.config.ListenerContainerFactoryBean#0
  4. 如何实现自己的机器做服务器
  5. React使用antd Table生成层级多选组件
  6. 没有bug队——加贝——Python 练习实例 23,24
  7. 网络流专题(完结撒花)
  8. Linux系统Bash(Shell)基础知识(4)
  9. R语言快速绘制多因素回归分析森林图(1)
  10. 计算流体动力学软件和服务市场现状研究分析-
  11. vue3+el-table表格表头增加斜线
  12. Python 实现的、带GUI界面的词云生成器
  13. DeeCamp 2020 挑战赛:通过深度学习识别生物电信号
  14. python大学考试题及答案填空_大学慕课2020Python编程基础题目及答案
  15. Ubuntu安装Clang\Clang版本切换
  16. Spring Boot pdf文件转图片
  17. java 引用 引用的对象,Java 引用类型简述
  18. Sequoia(基于JDBC的数据库集群中间件)用户手册
  19. 使用C#设置IE可信任站点
  20. A公司与B公司xx项目互通测试解决方案模板

热门文章

  1. Python中字典的介绍以及常用操作
  2. cass3d基础版_v1.1_仪表不凡说表:N厂“一劳永逸”V11版实至名归!
  3. android重新加载程序,从被杀死的Android应用程序中恢复
  4. win7如何设置通电自动开机_电脑自动开机,教您怎么设置电脑自动开机
  5. 【直播】今晚言有三阿里云直播、赠书+书籍解读+案例剖析
  6. 【知识星球】3D网络结构解读系列上新
  7. lazada做代运营,价格越低越好吗?价格和价值分析
  8. java与js交互,相互调用传参
  9. BS4xpath的使用
  10. windows安装两个版本JDK