hbs模板(zmazeui⽤的)
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⾏代码的模板引擎实现:,挺有意思的做法,当然⽤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 //判断是否是偶数
isterHelper('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的。举个栗⼦:
isterPartial('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);
ports = 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);
ports = 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库(github/donpark/hbs),⽽并⾮很多资料介绍的express-handlebars。hbs默认使⽤layout模板,实际上就是将你的模板⽂件替换掉{{{body}}}。layout是可配置的,可以在渲染选项中通过layout项来配置。
der('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');
isterPartials(__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}}
页⾯渲染的时候就是这样的:
der('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来帮我们完成了。很多时候我们要把
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论