hbs模板(zmaze ui用的)

一、总结

1、模板引擎:就是来生成界面的啊,只不过实现了view和数据分离以及一些其它的功能(预加载等)。

2、Handlebars :但他是一个单纯的模板引擎,在前端框架满天飞的年代感觉是有点弱了(比如thinkphp里面就有模板板块进行渲染)

3、thinkphp的模板板块的介绍:ThinkPHP内置了一个基于XML的性能卓越的模板引擎,这是一个专门为ThinkPHP服务的内置模板引擎,使用了XML标签库技术的编译型模板引擎,支持两种类型的模板标签,使用了动态编译和缓存技术,而且支持自定义标签库

4、hbs模板模板引擎语法:{{> header }},两个大括号前后端都可以使用

二、Handlebars 模板引擎之前后端用法

Handlebars 简介

先引用下百科的说法:

Handlebars 是 JavaScript 一个语义模板库,通过对view和data的分离来快速构建Web模板。它采用"Logic-less template"(无逻辑模版)的思路,在加载时被预编译,而不是到了客户端执行到代码时再去编译, 这样可以保证模板加载和运行的速度。

好吧,看了有点懵闭。这里关键词就是两个:无逻辑、预加载所有的模板引擎都是view和data分离,这点不用说。无逻辑准确点来说应该是弱逻辑,毕竟里面还是有一些if、each逻辑在的。你可能看过很多这样写的模板语言:

1 <% if (names.length) { %>
2 <ul>
3 <% names.forEach(function(name){ %>
4 <li><%= name %></li>
5 <% }) %>
6 </ul>
7 <% } %>

注:闭合的大括号一定不要忘了写哦。

看这种 js 与 HTML 的杂交写法我觉得很眼疼,我的眼里代码的可读性是非常重要的,这种写法真不是我的那杯茶!不过这种模板技术的实现方式倒是值得一探,推荐看看这个20行代码的模板引擎实现:http://blog.jobbole.com/56689/,挺有意思的做法,当然用eval也可以做。

Handlebar 的语法就简单精练了许多,比如上面的可以写成:

1 {{#if names.length}}
2 <ul>
3 {{#each names}}
4 <li>{{this}}</li>
5 {{/each}}
6 </ul>
7 {{/if}}

就喜欢这种一目了然的感觉,当然还有其他的swig、tx出的art-template之类的模板引擎,萝卜青菜各有所爱,就不多说了。

语法基础

语法很简单,就是用大括号将 data 包裹起来。其中两个 {{}} 会将内容做HTML编码转换,这里你输入的HTML标签代码什么的都会按你输入的字符输出;而三个 {{{}}} 的时候则不做转换,你在里面输入<h1>最后是真的能得到一个h1标签的。其他一些规则要素分别有:

1)块级

在 Handlebars里面,每个#就代表了一个局部块,每个块都有自身的作用域范围。举例来说:

1 // 数据
2 hehe: { words: 'hehehehe' }
3 yoyo: { words: 'yoyoyoyo'}

对应的模板:

1 {{#hehe}}
2 <p>{{words}}</p>
3 {{/hehe}}
4 {{#yoyo}}
5 <p>{{words}}</p>
6 {{/yoyo}}

这个例子很好理解,words属性都是根据自身的对象来输出的。这里还是按照块级作用域去理解会比较简单(虽然js并没有块级作用域。。。),也可以用this来指代当前对象。注意,即使是#if、#each也是有作用域的,不要跟js中的作用范围混为一谈。

2)路径

对于对象来说,你可以按照上文的例子一样直接使用 name 的 length 属性,还可以使用使用路径的表达方式去访问对象的其他层级。举个栗子:

 1 var post = {
2 title: "Blog Post!",
3 author: [{
4 id: 47,
5 name: "Jack"
6 },{
7 id: 20,
8 name: "Mark"
9 }]
10 };

模板要这么写:

 1 {{#post}}
2 {{#if author.length}}
3 <h3>{{title}}</h3>
4 <ul>
5 {{#each author}}
6 <li>{{../title}}'s author is {{name}}</li>
7 {{/each}}
8 </ul>
9 {{/if}}
10 {{/post}}

li标签里面已经是在 author 字段之内了,所以要使用 '../' 来转到上层的 title。

3)helper

上面其实已经用过helper了,内置的helper有if、each、unless、with等,当然你也可以自己去写helper。由于Handlebar的弱逻辑属性,如果要实现复杂一点的逻辑就需要去自定义helper。举个栗子:

 1 //判断是否是偶数
2 Handlebars.registerHelper('if_even', function(value, options) {
3 console.log('value:', value); // value: 2
4 console.log('this:', this); // this: Object {num: 2}
5 console.log('fn(this):', options.fn(this)); // fn(this): 2是偶数
6 if((value % 2) == 0) {
7 return options.fn(this);
8 } else {
9 return options.inverse(this);
10 }
11 });

helper是这样用的:

1 {{#if_even num}}
2 {{this.num}}是偶数
3 {{else}}
4 {{this.num}}是奇数
5 {{/if_even}}

当然输出你也能想到,就是根据奇数偶数输出相应信息。我们看看定义的一个function(value, options){},这个items就是我们使用模板时候的num,options是一些配置项,这里我们用到的是fn函数,这个函数执行的结果就是编译的结果(这里结果是“2是偶数”这一句话)。另外一个options.inverse就是取反,对应的就是模板里面的else语句了。

but:在模板中过度使用逻辑,实际上就是模糊了模板的专注点,这有违原本数据和表现分离的出发点。我还是认为模板应该专注数据绑定,逻辑应该在数据层做预处理,然后将结果返回给模板,而不是让模板去做各种数据的运算。

4)partial

使用模板引擎最重要的一点就是使用其partial功能,Handlebars里面是按照注册再使用的方式来管理partial的。举个栗子:

1 Handlebars.registerPartial('userMessage',
2 '<{{tagName}}>By {{author.firstName}} {{author.lastName}}</{{tagName}}>'
3 + '<div class="body">{{body}}</div>'
4 );

使用的时候就可以直接使用{{> userMessage}}将这个小块引入到页面中了。这里就是简单的局部替换,所以partial里面的data跟当前页面的data是在同一级的作用域内,也就是说你只要定义好author、body传进去就行了。tagName这个属于表现层的变量,应该在hbs文件里面进行声明,也即是{{> userMessage tagName="h1" }}这样使用。

在前端使用hbs

直接引入js的方式就不多说了,这里我是使用webpack来统一管理各种资源的。Handlebars对应的webpack插件为handlebars-loader,loader的配置非常简单:

1 {
2 test: /\.hbs$/,
3 loader: "handlebars"
4 }

Handlebars的后缀有两种,全称的handlebars以及简称的hbs,也可以直接用html,但还是跟普通html文件区分开来好一点。

使用模板的好处当然就是可以组件化开发了。我这里采用的目录是这样的:

其中页面组件指的是应用中的页面单元,页面是由各种控件组件组成的,这些都已经是共识了,就不再赘述了。引用的方法有几种:

(1)因为Handlebar编译出来的只是一个字符串,所以我们可以用js作为入口去管理组件,每个组件的js文件引入相应的css和模板,输出为dom字符串。页面引用组件的时候就直接引用js模块得到dom字符串,然后将dom字符串渲染到相应的{{{}}}中去。这种js大一统的方式跟现在主流框架的做法是一样的,可以将逻辑、样式、内容和资源统一起来管理,组件也得内聚性比较强。

 1 // header.js
2 require('./header.scss');
3 var headerTpl = require('./header.hbs');
4 var data = {words: "This is header!"}; //data可以用参数传入
5 var header = headerTpl(data);
6 module.exports = header;
7
8 // home.hbs
9 <div class="home">
10 {{{ header }}}
11 <h2>This is {{name}} page.</h2>
12 {{{ footer }}}
13 </div>
14
15 // home.js
16 require('./home.scss');
17 var header = require('../../component/header/header.js');
18 var footer = require('../../component/footer/footer.js');
19 var homeTpl = require('./home.hbs');
20 var data = {
21 header: header,
22 footer: footer,
23 name: 'home'
24 };
25 var home = homeTpl(data);
26 module.exports = home;

(2) 另外的方案就是使用局部模板的方式了,这种方式对一些不带js逻辑的组件非常合适,比如页头页尾这些纯内容的组件。在hbs里面可以直接按照路径去引用particle,然后把引入组件的时候提供partial所需的数据,例如home页面就是这样的:

<div class="home">
{{> ../../component/header/header}}
<h2>this is {{}} page</h2>
{{> ../../component/footer/footer}}
</div>

既然我们已经用了webpack来管理,当然也可以让webpack来处理引用路径了,这里只需要在配置里面声明partial的路径即可直接引用,loader配置:

 1 {
2 test: /\.hbs$/,
3 loader: "handlebars",
4 query: {
5 partialDirs: [
6 path.join(SRC_PATH, 'component', 'header'),
7 path.join(SRC_PATH, 'component', 'footer'),
8 path.join(SRC_PATH, 'page', 'home')
9 ]
10 }
11 }

模板文件:

<div class="home">
{{> header }}
<h2>This is {{name}} page.</h2>
{{> footer }}
</div>

上面列出的几种方式各有优劣,使用partial的方式可以将相应的模板文件集中放到一个view文件夹里面,partialDirs就不用写一大堆路径了。个人还是更偏向于使用第一种方式,每个组件的css、html、js文件做成一个整体的方式,遵循就近管理原则。

Nodejs后端使用hbs

Node后端使用hbs也非常方便,这里我用的是express框架,直接后端渲染。当然更精细的做法就是首屏渲染、仅移动端后端渲染了,在这种混搭的场合模板是可以通用的,这样就减少了一定的开发工作量。目前express中自带4种模板引擎,jade、esj、hogan与hbs,我是使用express-generator来生成项目脚手架的,输入命令为: express --hbs 项目名。

express-generator中的hbs用的是hbs库(https://github.com/donpark/hbs),而并非很多资料介绍的express-handlebars。hbs默认使用layout模板,实际上就是将你的模板文件替换掉{{{body}}}。layout是可配置的,可以在渲染选项中通过layout项来配置。

1 res.render('index', {
2 title: 'Express',
3 head: '<h1>head part</h1>',
4 layout: true //默认为true,设为false则不启用layout模板
5 });

目前我所接触到的hbs项目都是express+hbs+zepto/jq这一套,如果有用其他前端框架的话,一般也不会用到hbs了,所以只说说这种情况。后端渲染跟前端渲染的开发模式略有差异,但思路还是一样的要做组件化开发。上文说过前端使用hbs的时候是以js为入口,而在后端使用hbs的话个人认为更适合使用局部模板的方式。

我的目录是这样的:

页面统一放入views中,局部模板放入views/partial里面。js和css还是按照官方默认的方式集中管理。使用局部模板要先注册,需要在app.js这个服务器脚本里面加入以下代码:

1 var hbs = require('hbs');
2 hbs.registerPartials(__dirname + '/views/partials');

模板文件中引入小模板:

 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>{{title}}</title>
5 <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
6 <link rel="stylesheet" href="/css/main.css"/>
7 {{> resource}}
8 </head>
9 <body>
10 {{> header}}
11 <div class="container">
12 {{{body}}}
13 </div>
14 {{> footer}}
15 </body>
16 </html>

resource模板主要是控制不同页面引入的不同资源:

1 {{#each css}}
2 <link rel='stylesheet' href={{this}} />
3 {{/each}}
4 {{#each js}}
5 <script src={{this}}></script>
6 {{/each}}

页面渲染的时候就是这样的:

1 res.render('index', {
2 title: 'Express',
3 css: ['/css/home.css', '/css/home_add.css'],
4 js: ['/js/home.js'],
5 name: "茄果" //这个是页面中用到的数据,与title同一性质
6 });

这种方式的一个问题就是css、js这些资源的写法跟我们平常直接在html引用的方式不一样。比如我想资源引用写在页面中,比如home.hbs里面,如果直接写入home.hbs里面的话,内容是直接插入到{{{body}}}的位置,但我们想要的是在head的位置啊。这个如何实现呢?之前我们定义partial只是为了简单的替换,这一次除了替换还要做一个插入dom的操作,这个就要用到helper来帮我们完成了。很多时候我们要把css放入头部,而把js放在页面尾部,所以layout文件要改造成:

 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>{{title}}</title>
5 <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
6 <link rel="stylesheet" href="/css/main.css"/>
7 {{#each cssList}}
8 <link rel='stylesheet' href={{this}} />
9 {{/each}}
10 </head>
11 <body>
12 {{> header}}
13 <div class="container">
14 {{{body}}}
15 </div>
16 {{> footer}}
17 {{#each jsList}}
18 <script src={{this}}></script>
19 {{/each}}
20 </body>
21 </html>

现在只要注册helper把hbs文件中定义的值传入cssList、jsList中即可。另外还要考虑到页面组件引用的时候可能会出现重复依赖的情况,所以要做一个去重的工作。注册helper与注册partial一样,都要写在app.js文件中,下面给出css的写法,js也类似:

 1 hbs.registerHelper('css', function(str, option) {
2 var cssList = this.cssList || [];
3 str = str.split(/[,,;;]/);
4 console.log('css: ',str);
5 str.forEach(function (item) {
6 if(cssList.indexOf(item)<0) {
7 cssList.push(item);
8 }
9 });
10 this.cssList = cssList.concat();
11 });

页面中引入css、js就应该是这样:

{{css "/css/home_add.css"}}
{{js "/js/home.js"}}
{{> p}}
<p>This is home page!</p>

上面例子中的局部模板p作为一个组件,也引用了相应的css和js,写法跟页面的写法是一样的。

总结

handlebars作为一个非常轻量级的模板引擎,单纯从模板这个功能上看,他的前后端通用性强,命令简单明了,代码可读性强。但他是一个单纯的模板引擎,在前端框架满天飞的年代感觉是有点弱了。无论是前端还是后端,各种大框架都有渲染模块。当然不喜欢大框架的全家桶倒是可以考虑使用handlebars,所以主要还是要看项目吧。青菜萝卜,各有所好。

hbs模板(zmaze ui用的)的更多相关文章

  1. tornado 学习笔记8 模板以及UI

          Tornado 包含一个简单.快速而且灵活的模板语言.       Tornado同样可以使用任何其他的python模板语言,虽然没有集成这些模板语言进RequestHandler.ren ...

  2. 模板应用--UI线程与worker线程同步 模仿c# invoke

    由之前的一篇博文 <UI线程与worker线程><UI线程与worker线程>引出,UI线程与worker线程“串行化”在win32上实现是多么没有节操的事情,代码编写麻烦不说 ...

  3. Kendo UI开发教程(7): Kendo UI 模板概述

    Kendo UI 框架提供了一个易用,高性能的JavaScript模板引擎.通过模板可以创建一个HTML片段然后可以和JavaScript数据合并成最终的HTML元素. Kendo 模板侧重于UI显示 ...

  4. Kendo UI 模板概述

    Kendo UI 模板概述 Kendo UI 框架提供了一个易用,高性能的 JavaScript 模板引擎.通过模板可以创建一个 HTML 片段然后可以和 JavaScript 数据合并成最终的 HT ...

  5. Handlebars的基本用法 Handlebars.js使用介绍 http://handlebarsjs.com/ Handlebars.js 模板引擎 javascript/jquery模板引擎——Handlebars初体验 handlebars.js 入门(1) 作为一名前端的你,必须掌握的模板引擎:Handlebars 前端数据模板handlebars与jquery整

    Handlebars的基本用法 使用Handlebars,你可以轻松创建语义化模板,Mustache模板和Handlebars是兼容的,所以你可以将Mustache导入Handlebars以使用 Ha ...

  6. 在RichFaces中使用Facelets模板

    在RichFaces中使用Facelets模板 目录 Facelets简介 Facelets标签 创建相应文件 Facelets简介 Facelets是用来构建JSF应用程序的默认视图技术.它为表现层 ...

  7. Hybrid框架UI重构之路:三、工欲善其事,必先利其器

    上文回顾:Hybird框架UI重构之路:二.事出有因 工欲善其事,必先利其器,事是重构的目标,器是开发环境. 这篇文章将讲述重构时的UI框架的目录结构,且需要使用的开发工具. 目录结构 demo : ...

  8. JSF的ui标签

    在使用自己的tag时,首先需要在web.xml里面进行注册,注册方式是在web.xml开头加上:  <context-param>        <param-name>fac ...

  9. 分享3一个博客HTML5模板

    1.材类别:半透明 博客html模板 个人博客 半透明html5博客主题,半透明,博客,博客html模板,个人博客,html5,灰色,半透明html5博客主题是一款适合用于个人博客主题,风格非常不错. ...

随机推荐

  1. SpringMVC中JSP取不到ModelAndView的数据原因

    自己搭的项目突然EL表达式取不到值了 不管是用 ${msg} 还是用JSTL的<c:out value="${msg}"/> 都不行 才发现犯了一个平时不会注意的错误 ...

  2. JWT Authentication Tutorial: An example using Spring Boot--转

    原文地址:http://www.svlada.com/jwt-token-authentication-with-spring-boot/ Table of contents: Introductio ...

  3. 一张图了解javaJwt

    1.什么是javaJwt? JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims ...

  4. 通过视频展示如何通过Samba配置PDC

    通过视频展示如何通过Samba配置PDC(Linux企业应用案例精解补充视频内容) 本文通过视频,真实地再现了在Linux平台下如何通过配置smb.conf文件而实现Samba Server模拟win ...

  5. SQL 小技能

    1. SET STATISTICS TIMEON   附带原文 2.关于索引,是自己可以用Tsql语句建,也可以在设计表的时候选中某一个字段建立索引   一般而言,主键属于聚合索引(字典:A-Z);反 ...

  6. MYSQL查询数据中通讯最多的两个人

    SELECT GREATEST( sender_id, recipient_id ) , LEAST( sender_id, recipient_id ) , COUNT( * ) AS aFROM ...

  7. 【Uva 1631】Locker

    [Link]: [Description] 有一个n(n≤1000)位密码锁,每位都是0-9,可以循环旋转.每次可以让1-3个相邻 数字同时往上或者往下转一格.例如,567890->567901 ...

  8. 洛谷 P1885 Moo

    P1885 Moo 题目描述 奶牛Bessie最近在学习字符串操作,它用如下的规则逐一的构造出新的字符串: S(0) = “moo” S(1) = S(0) + “m”+ “ooo” + S(0) = ...

  9. hdu 5073 Galaxy(2014 鞍山现场赛)

    Galaxy                                                                   Time Limit: 2000/1000 MS (J ...

  10. [ES2017] Iterate over properties of an object with ES2017 Object.entries()

    The Object.entries() function is an addition to the ECMAscript scpec in Es2017. This allows us to it ...