찾다
웹 프론트엔드HTML 튜토리얼handlebars玩家指南_html/css_WEB-ITnose

提示:文中 “{ {” 、“} }”,在实际使用中用双花括号,中间无空格。

写这篇文章也是因为当初我为了实现一个模板分页的某个功能(这个问题以及解决方法最后说),花了不少时间将官网看了几遍,试验了很久之后依然没有成功,然后凭着一股崛起继续钻研终于解决了这个问题。此时发现handlebars的东西基本上也了解得差不多了,所以干脆抽时间写篇文章整理一下。

为什么需要模板引擎

关于前端的模板引擎,我用一个公式来解释

            模板引擎 模板 + 数据 ========> html页面

模板引擎就像是html的解析生成器,将对应的模板填充完数据之后生成静态的html页面。它可以在浏览器端(比如angular中指令所用的模板)也可以在服务器端执行,不过一般用于服务器端。因为它的一个作用是抽象公共页面来重用,如果在服务端填充数据,可以减少回填数据给页面的ajax请求,从而提升浏览器端整体页面渲染速度。

那些年我用过的模板引擎

接触过的模板引擎不算多,最早应该是jsp,本质上也是一种模板引擎,再到功能稍微强大的freemarker,这两种都是属于java语系的。js语系的jade和ejs我都有所接触,不过不常用,jade那种类python的语法规则以及较低的解析效率都让我不敢兴趣,Express框架也只是早起将其作为模板引擎。后来换成了强大的ejs,无论是功能还是写法上都接近jsp了。直到最新的Express4发布,默认改为了弱逻辑的比较简洁的模板引擎handlebars。

我使用handlebars有以下几个原因:

  • 这次新项目前端框架搭建基于Express4,模板引擎只能在ejs/jade/hogan/hbs中选择一个。
  • 默认是handlebars,虽不知道原因,想必有其原因。
  • 看过“去哪儿”的前端技术分享,他们就是在handlebars上进行封装的,证明已经有人填过坑了,可以一试。
  • 开始比较看好ejs,但是官网文档被强了,相比之下handlebars的文档比较清晰,还有实例,虽然逻辑结构比较混乱,但是基本无障碍。

码解handlebars

运行环境:Express4、hbs4

未接触Express或hbs的可以先看 这里

初级玩家:表达式

数据:

{     title: 'Express',     obj:{        version: 'v4.3',         category: 'node',         "date~": '2016'    }}

模板:

<p>{ {title} }</p><p>{ {obj.version} }</p><p>{ {obj/category} }</p><p>{ {obj.date~} }</p>

html页面:

Expressv4.3node

handlebars中变量都添加双花括号来表示(类似Angular),对比ejs的””来说看起来没什么区别,其实这是很人性化的,想一下你键盘上的位置,再考虑按这几个字符的难易程度你就懂了。其中要访问变量的属性值时可以用类似json格式的”.”,也可以用”/“。

其中变量名不可包含以下字符。如果包含则不被解析,如上的”“。

空格 ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~

但可以用 , , [] 来转译这些特殊字符。

这一条规则意味着 “&&”,”||”,”!”这类逻辑判断是不能出现在表达式中的! (看着这一条是不是觉得弱爆了,要不然怎么叫若逻辑模板引擎呢~哈哈,不过当然有另外的解决办法)。

中级玩家:helper

英语水平有限,实在找不到一个恰当的词来翻译它了。可以理解为它是注入到模板中的一个函数,用来接收参数并进行逻辑处理。

默认helper

if else

{ {#if author} }  <h1 id="firstName-lastName">{ {firstName} } { {lastName} }</h1>{ {else} }  <h1 id="Unknown-Author">Unknown Author</h1>{ {/if} }

{ {#if isActive} } <img src="/static/imghwm/default1.png"  data-src="star.gif"  class="lazy" alt="Active">{ {else if isInactive} } <img src="/static/imghwm/default1.png"  data-src="cry.gif"  class="lazy" alt="Inactive">{ {/if} }

和一般的编程语言的 if-else 代码块是差不多的,不过再次重申由于上面提到的特殊字符,所以if条件中是不能有逻辑表达式的,只能是变量或者值。

unless

还是因为上面提到的那些字符,handlebars不支持逻辑非(“!”),所以又有了一个与if相反的helper

{ {#unless license} }<h3 id="WARNING-This-entry-does-not-have-a-license">WARNING: This entry does not have a license!</h3>{ {/unless} }

上面这段代码就等价于

{ {#if license} }{ {else} }<h3 id="WARNING-This-entry-does-not-have-a-license">WARNING: This entry does not have a license!</h3>{ {/if} }

each

都知道each相当于for循环。不过有些地方需要注意:

  • 可以用相对路径的方式来获取上一层的上下文。(上下文概念跟js中的上下文差不多,比如在each passage代码块内,每一次循环上下文一次是passage[0],passage[1]…)
  • 一些默认变量,@first/@last 当该对象为数组中第一个/最后一个时返回真值。如果数组成员为值而非对象,@index表示当前索引值,可以用@key或者this获取当前值
  • 可以用 as |xxx| 的形式给变量起别名,循环中通过别名可以引用父级变量值。当然也可以通过相对路径的方式引用父级变量。

    { {#each passage} }    { {#each paragraphs} }      { {@../index} }:{ {@index} }:{ {this} }</p>    { {else} }      <p class="empty">No content</p>    { {/each} }{ {/each} }

{ {#each array as |value, key|} }  { {#each child as |childValue, childKey|} }    { {key} } - { {childKey} }. { {childValue} }  { {/each} }{ {/each} }

同时也可以用来遍历对象,这时@key表示属性名,this表示对应的值

{ {#each object} }  { {@key} }: { {this} }{ {/each} }

with

类似js中的with,可以配合分页使用,限定作用域。

{ {#with author as |myAuthor|} }  <h2 id="By-myAuthor-firstName-myAuthor-lastName">By { {myAuthor.firstName} } { {myAuthor.lastName} }</h2>{ {else} }  <p class="empty">No content</p>{ {/with} }

lookup

这个用于以下这种并列数组的情况,可以按照索引来找兄弟变量对应的值。理解起来有些困难,直接看代码

{ groups: [        {id: 1, title: "group1"},        {id: 2, title: "group2"},    ], users: [        {id:1, login: "user1", groupId: 1},        {id:2, login: "user2", groupId: 2},        {id:3, login: "user3", groupId: 1}    ], infos: [        'a','b','c'    ]}

<table> { {#each users} } <tr data-id="{ {id} }"> <td>{ {login} }</td> <td data-id="{ {groupId} }">{ {lookup ../infos @index} }</td> </tr> { {/each} }</table>

user1   auser2   buser3   c

这里在users数组中按照索引值引用infos数组中对应的值,如果想引用groups中的groupId呢?很简单,用with。

<table> { {#each users} } <tr data-id="{ {id} }"> <td>{ {login} }</td> <td data-id="{ {groupId} }">{ {#with (lookup ../groups @index)} }{ {title} }{ {/with} }</td> </tr> { {/each} }</table>

自定义helper

内置的helper不够强大,所以通常需要写js代码自定义helper,先看一个简单的单行helper。

行级helper

传值

数值、字符串、布尔值这种常规数据可以直接传入,同时也可以传递JSON对象(但只能传一个),以key=value这种形式写在后面,最后就可以通过参数的hash属性来访问了。

模板

{ {agree_button "My Text" class="my-class" visible=true counter=4} }

代码

hbs.registerHelper('agree_button', function() { console.log(arguments[0]);//==>"My Text" console.log(arguments[1].hash);//==>{class:"my-class",visible:true,conter:4}}

传变量

传变量时可以用this指针来指代它访问属性,通过逻辑判断后可以返回一段html代码,不过太建议这样做。考虑以后的维护性,这种html代码和js代码混合起来的维护性是比较差的,如果要抽象层组件还是使用分页比较好。

模板:

{ {agree_button person} }

注册helper:

hbs.registerHelper('agree_button', function(p) {  console.log(p===this);//==> true  var blog = hbs.handlebars.escapeExpression(this.person.blog),      name = hbs.handlebars.escapeExpression(this.person.name);  return new hbs.handlebars.SafeString(    "<a href='"+blog+"'>"+ name + "</button>"  );});

数据:

var context = {    person:{name: "亚里士朱德", blog: "https://yalishizhude.github.io"} };};

html页面:

<a href="https://yalishizhude.github.io">亚里士朱德</a>

当内容只想做字符串解析的时候可以用 escapeExpressionSafetString 函数。

块级helper

块级helper获取参数的方式跟之前差不多,只是最后多了一个参数,这个参数有两个函数 fn 和 revers 可以和 else 搭配使用。后面将会讲解。

模板:

{ {#list nav} }  <a href="{ {url} }">{ {title} }</a>{ {/list} }

注册helper:

Handlebars.registerHelper('list', function(context, options) {  var ret = "<ul>";  for(var i=0, j=context.length; i<j; i++) {    ret = ret + "<li>" + options.fn(context[i]) + "</li>";  }  return ret + "</ul>";});

数据:

{ nav: [ { url: "https://yalishihzude.github.io", title: "blog" }, { url: "https://www.github.com/yalishizhude", title: "github" }, ]}

html页面:

<ul>    <li>  <a href="https://yalishizhude.github.io">blog</a> </li>    <li>  <a href="https://www.github.com/yalishizhude">github</a> </li></ul>

自定义helper

each的index变量比较常用,但是它是从0开始的,往往不符合业务中的需求,这里写个helper来扩展一下。

注册helper:

hbs.registerHelper('eval', function(str, options){    var reg = /\{\{.*?\}\}/g;    var result = false;    var variables = str.match(reg);    var context = this;    //如果是each    if(options.data){      context.first = context.first||options.data.first;      context.last = context.last||options.data.last;      context.index = context.index||options.data.index;      context.key = context.key||options.data.key;    }    _.each(variables, function(v){      var key = v.replace(/{ {|} }/g,"");      var value = typeof context[key]==="string"?('"'+context[key]+'"'):context[key];      str = str.replace(v, value);    });    try{      result = eval(str);      return new hbs.handlebars.SafeString(result);    }catch(e){      return new hbs.handlebars.SafeString('');      console.log(str,'--Handlerbars Helper "eval" deal with wrong expression!');    }  });

模板:

{ {#each list} }{ {eval '{ {index} }+1'} }{ {/each} }

上面说到if不支持复杂的表达式,如果是“&&”操作还可以用子表达式来实现,更加复杂的就不好办了,这里我写了一个helper来实现。

注册helper:

hbs.registerHelper('ex', function(str, options) {    var reg = /\{\{.*?\}\}/g;    var result = false;    var variables = str.match(reg);    var context = this;    _.each(variables, function(v){      var key = v.replace(/{ {|} }/g,"");      var value = typeof context[key]==="string"?('"'+context[key]+'"'):context[key];      str = str.replace(v, value);    });    try{      result = eval(str);      if (result) {        return options.fn(this);      } else {        return options.inverse(this);      }    }catch(e){      console.log(str,'--Handlerbars Helper "ex" deal with wrong expression!');      return options.inverse(this);    }  });

模板:

{ {#ex "{ {state} }==='submiting'"} }<i class="icon cross-danger">1</i>{ {else} }<i class="icon cross-success">2</i>{ {/ex} }

先将整个逻辑表达式作为一个字符串传入,然后替换其中的变量值,最后用eval函数来解析表达式,同时增加异常处理。

高级玩家:partial

比较推崇使用分页来实现组件化。分页跟helper一样需要先注册。在hbs模块中可以批量注册,比较简单。

hbs.registerPartials(__dirname + '/views/partials');

基础引用

用“>”来引用模板,这种情况一般用来处理页头页尾这种简单的分页。后面可以传入参数。

{ {> myPartial param} }

当使用块级表达式时,我们通常添加“#”,而分页是“>”,所以块级分页使用“#>”,这里表示如果layout分页不存在则显示块内的内容My Content。

{ {#> layout } }  My Content{ {/layout} }

动态分页

当然也可以用表达式来代替分页名称

{ {> (whichPartial) } }

当分页中一部分代码是固定的,另一部分是变化的时候,可以在分页中添加“@partial-block”,这时当引用这个分页时,在内部编写代码将会填充到这个位置。

partial.hbs:

亚里士朱德{ {> @partial-block } }

模板:

{ {#>partial} }https:yalishizhude.github.io{ {/partial} }

html页面:

亚里士朱德https:yalishizhude.github.io

内联分页

当有多段代码需要填充到分页时,可以用如下方法。分页中内嵌分页变量,模板中通过内联分页的方式传入。

模板:

{ {#> partial} }  { {#*inline "nav"} }    亚里士朱德  { {/inline} }  { {#*inline "content"} }    https://yalishizhude.github.io  { {/inline} }{ {/partial} }

partial.hbs:

<div class="nav">  { {> nav} }</div><div class="content">  { {> content} }</div>

html页面:

<div class="nav">    亚里士朱德</div><div class="content">    https://yalishizhude.github.io</div>

大师级玩家:API

本文列举的只是handlebars中最重要和常用的功能,更多细碎的功能可以去查看

官方API 。

开头的问题

我想将导航条写成一个分页(partial),导航条左边的文字标题是可以通过参数传递的,但是右边的内容可能是文字、图片其它元素,需要具体业务自定义实现。我又不想把html代码写在js中,所以希望在模板中将这段未知的模板代码填充到分页中进行展现。我在官网文档中找到了 NaN来实现此功能,但是本机实验一直解析报错。

解决过程:

这个问题原因可能有两个,一是官方文档有错,二是本机环境的插件有问题(Express用hbs模块,该模块封装了handlebars引擎模块)。为了验证官方文档的正确性,我找到了一个在线handlebars解析器,输入文档中的代码时可以正确解析,那么只可能出现在hbs模块了。这时在github上找到hbs模块最新版本为4,查看本地版本为3,更新后果然可以正常解析了。

总结

handlebars让我们看到一个好的插件应该有的特征:

  • 可识别性。接口简单,使用方便,容易上手。
  • 高可用性。自带常用一些功能(helper),不求多而求精。
  • 可扩展性。复杂的业务逻辑,开发人员可以自定义helper去扩展和实现。

作者:亚里士朱德

博客: http://yalishizhude.github.io

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
HTML의 부울 속성은 무엇입니까? 몇 가지 예를 들어주십시오.HTML의 부울 속성은 무엇입니까? 몇 가지 예를 들어주십시오.Apr 25, 2025 am 12:01 AM

부울 속성은 값없이 활성화되는 HTML의 특수 속성입니다. 1. 부울 속성은 입력 상자를 비활성화하는 등의 존재 여부에 따라 요소의 동작을 제어합니다. 2. 작업 원칙은 브라우저가 구문 분석 할 때 속성의 존재에 따라 요소 동작을 변경하는 것입니다. 3. 기본 사용법은 속성을 직접 추가하는 것이며, 고급 사용량은 JavaScript를 통해 동적으로 제어 될 수 있습니다. 4. 일반적인 실수는 값을 설정해야한다고 잘못 생각하고 올바른 글쓰기 방법은 간결해야합니다. 5. 모범 사례는 코드를 간결하게 유지하고 부울 속성을 합리적으로 사용하여 웹 페이지 성능 및 사용자 경험을 최적화하는 것입니다.

HTML 코드를 어떻게 검증 할 수 있습니까?HTML 코드를 어떻게 검증 할 수 있습니까?Apr 24, 2025 am 12:04 AM

HTML 코드는 온라인 유효성 검사기, 통합 도구 및 자동화 된 프로세스를 통해 깨끗할 수 있습니다. 1) w3cmarkupvalidationservice를 사용하여 온라인으로 HTML 코드를 확인하십시오. 2) 실시간 확인을 위해 VisualStudioCode에 HTMLHINT 확장을 설치하고 구성하십시오. 3) htmltidy를 사용하여 시공 프로세스에서 HTML 파일을 자동으로 확인하고 청소하십시오.

HTML vs. CSS 및 JavaScript : 웹 기술 비교HTML vs. CSS 및 JavaScript : 웹 기술 비교Apr 23, 2025 am 12:05 AM

HTML, CSS 및 JavaScript는 최신 웹 페이지를 구축하기위한 핵심 기술입니다. 1. HTML 웹 페이지 구조를 정의합니다. 2. CSS는 웹 페이지의 모양을 담당합니다.

마크 업 언어로서의 HTML : 기능과 목적마크 업 언어로서의 HTML : 기능과 목적Apr 22, 2025 am 12:02 AM

HTML의 기능은 웹 페이지의 구조와 내용을 정의하는 것이며, 그 목적은 정보를 표시하는 표준화 된 방법을 제공하는 것입니다. 1) HTML은 타이틀 및 단락과 같은 태그 및 속성을 통해 웹 페이지의 다양한 부분을 구성합니다. 2) 콘텐츠 및 성능 분리를 지원하고 유지 보수 효율성을 향상시킵니다. 3) HTML은 확장 가능하므로 사용자 정의 태그가 SEO를 향상시킬 수 있습니다.

HTML, CSS 및 JavaScript의 미래 : 웹 개발 동향HTML, CSS 및 JavaScript의 미래 : 웹 개발 동향Apr 19, 2025 am 12:02 AM

HTML의 미래 트렌드는 의미론 및 웹 구성 요소이며 CSS의 미래 트렌드는 CSS-In-JS 및 CSShoudini이며, JavaScript의 미래 트렌드는 WebAssembly 및 서버리스입니다. 1. HTML 시맨틱은 접근성과 SEO 효과를 향상시키고 웹 구성 요소는 개발 효율성을 향상 시키지만 브라우저 호환성에주의를 기울여야합니다. 2. CSS-in-JS는 스타일 관리 유연성을 향상 시키지만 파일 크기를 증가시킬 수 있습니다. CSShoudini는 CSS 렌더링의 직접 작동을 허용합니다. 3. Webosembly는 브라우저 애플리케이션 성능을 최적화하지만 가파른 학습 곡선을 가지고 있으며 서버리스는 개발을 단순화하지만 콜드 스타트 ​​문제의 최적화가 필요합니다.

HTML : 구조, CSS : 스타일, 자바 스크립트 : 동작HTML : 구조, CSS : 스타일, 자바 스크립트 : 동작Apr 18, 2025 am 12:09 AM

웹 개발에서 HTML, CSS 및 JavaScript의 역할은 다음과 같습니다. 1. HTML은 웹 페이지 구조를 정의하고, 2. CSS는 웹 페이지 스타일을 제어하고 3. JavaScript는 동적 동작을 추가합니다. 그들은 함께 현대 웹 사이트의 프레임 워크, 미학 및 상호 작용을 구축합니다.

HTML의 미래 : 웹 디자인의 진화 및 트렌드HTML의 미래 : 웹 디자인의 진화 및 트렌드Apr 17, 2025 am 12:12 AM

HTML의 미래는 무한한 가능성으로 가득합니다. 1) 새로운 기능과 표준에는 더 많은 의미 론적 태그와 WebComponents의 인기가 포함됩니다. 2) 웹 디자인 트렌드는 반응적이고 접근 가능한 디자인을 향해 계속 발전 할 것입니다. 3) 성능 최적화는 반응 형 이미지 로딩 및 게으른로드 기술을 통해 사용자 경험을 향상시킬 것입니다.

HTML vs. CSS vs. JavaScript : 비교 개요HTML vs. CSS vs. JavaScript : 비교 개요Apr 16, 2025 am 12:04 AM

웹 개발에서 HTML, CSS 및 JavaScript의 역할은 다음과 같습니다. HTML은 컨텐츠 구조를 담당하고 CSS는 스타일을 담당하며 JavaScript는 동적 동작을 담당합니다. 1. HTML은 태그를 통해 웹 페이지 구조와 컨텐츠를 정의하여 의미를 보장합니다. 2. CSS는 선택기와 속성을 통해 웹 페이지 스타일을 제어하여 아름답고 읽기 쉽게 만듭니다. 3. JavaScript는 스크립트를 통해 웹 페이지 동작을 제어하여 동적 및 대화식 기능을 달성합니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

SecList

SecList

SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전

DVWA

DVWA

DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

안전한 시험 브라우저

안전한 시험 브라우저

안전한 시험 브라우저는 온라인 시험을 안전하게 치르기 위한 보안 브라우저 환경입니다. 이 소프트웨어는 모든 컴퓨터를 안전한 워크스테이션으로 바꿔줍니다. 이는 모든 유틸리티에 대한 액세스를 제어하고 학생들이 승인되지 않은 리소스를 사용하는 것을 방지합니다.