微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

利用 handlebars 实现后端组件化

2016-05-07 20:18 前端大全

(点击上方公众号,可快速关注)


作者:伯乐在线专栏作者 - 亚里士朱德

链接:http://web.jobbole.com/85948/

点击 → 了解如何加入专栏作者


基本说明(使用过exress和handlebars的可以略过):express中的handlebars引擎是这么生成页面的:


/* layout.hbs

* 主模板,所有的的页面都将替换"{{{body}}}""{{}}"相当于占位符,由数据进行替换

*/

<!DOCTYPE html>

<html>

  <head>

    <title>{{title}}</title>

  </head>

  <body>

    {{{body}}}

  </body>

</html>

 

/* index.hbs

* 单个页面模板,这里以首页为例。"{{>}}"表示引用其他模板来替换,这里引用名为"partial"的模板

*/

<div>index</div>

{{>partial}}

 

/* partial.hbs

* 一个分页文件,被其他模板引用,分页之间也可以互相引用。

*/

<div>123</div>

 

/* index.html

* 当浏览器请求index.html时,经过handlebars模板引擎处理后生成的页面

*/

<!DOCTYPE html>

<html>

   <head>

     <title></title>

   </head>

   <body>

     <div>index</div>

     <div>123</div>

   </body>

</html>


实现步骤1:模板


react的火热让“组件化”的概念持续升温,不过组件化确实在开发中提供了高可复用的代码,大大减少了工作量和bug,确实值得提倡。举个例子。


<ul class="titles border" id="navigator">

  <li class="title">标题1</li>

  <li class="title">标题2</li>

  <li class="title">标题3</li>

</ul>


这一段html代码,会在几个页面用到,如果按照一般的做法把这段代码ctrl-c、ctrl-v到要用的页面。就会出现两个问题:1.重复代码增多(ctrl-c、ctrl-v应该是程序员的大忌),当然这不是最重要的,最重要的是第2点——维护性差。如果现在我要把“标题1”改成“标题0”,那么只能进行全量搜索然后替换,不仅操作麻烦而且容易出错。 如果用到了模板技术的话这个问题就很好解决,把上面那一段代码写成一个模板,在handlebars中我们成为分页,然后需要这段的代码的页面引用这个分页,如果要修改的话直接修改分页了。以handlebars为例:


//navigator.hbs

<ul class="titles border" id="navigator">

  <li class="title">标题1</li>

  <li class="title">标题2</li>

  <li class="title">标题3</li>

</ul>

 

//在index.hbs中引用

{{>navigator}}


为什么handlebars?本文所用的后端模板引擎都以handlebars为例,原因是上次听去哪儿前端团队做的关于node.js的技术分享,炫耀了一个自己基于handlebars实现的小功能:分页中引用的css文件可以全部放到head中。心中一直觊觎这个小功能,直到最近和“组件化”的概念结合在一起考虑,发现这个功能对于实现后端的组件化很有帮助。自己对handlebars也略有研究,所以试着用handlebars来实现一下“组件化”。


实现步骤2:打包


这样就完美了么?no~no~no~ 上面的这一段html代码中可是有样式的,按照w3c的规范,样式应该写在css文件中,怎么实现?自然而然想到两种解决办法:


  1. 在分页中加入link标签来引入所需的样式,想一想html代码中到处穿插link标签是什么感觉~且不说生成页面难以维护,浏览器渲染速度也会受影响。

  2. 把分页所需的样式放在公共的样式文件中,这是目前我们项目的通用做法,纯粹的懒人策略,缺点很明显,很多页面引用了一些无用的样式,浪费网络带宽,尤其当项目变大时这个缺点将更加明显。 所以最好的解决方法是按需加载,只加载引用组件所需的样式,当然样式文件按分页拆分得这么细的话会增加请求数,影响不会太大,如果想优化的话也可以压缩合并成一个请求,这个后面再说。 handlerbas中常见的扩展方式就是编写helper,我们可以编写一个helper,


//app.js

hbs.registerHelper('css', function(str, option) {

  //在上下文中创建一个数组用来保存该页面需要用到的css文件

  this.cssList = this.cssList || [];  

  this.cssList.push(str);

});


这个helper的作用就是注册一个名为”css”的helper,帮我们保存分页中用到的css文件地址。然后我们在主模板layout的head标签部分遍历cssList数组循环加载出来。


//layout.hbs

<head>

  <title>{{title}}</title>

 

  {{#each cssList}}

  <link rel="stylesheet" href="{{this}}" media="screen" title="no title" charset="utf-8">

  {{/each}}

</head>

...


同时原来的分页改成


//navigator.hbs

{{css '/stylesheets/components/navigator.css'}}

<ul class="titles border" id="navigator">

  <li class="title">标题1</li>

  <li class="title">标题2</li>

  <li class="title">标题3</li>

</ul>


上面写的只是一个简单的无逻辑的静态组件,有些组件可能会有交互效果,比如处理一些点击事件或者对外暴露可操作的接口等,那么就需要js逻辑来实现了。


实现步骤3:逻辑


其实实现原理也大同小异,也是先注册一个helper,然后在主模板layout中添加,这里我们在原来的分页中引入一个js文件。具体代码如下:


//app.js

hbs.registerHelper('js', function(str, option) {

  this.jsList = this.jsList || [];

  this.jsList.push(str);

});


//layout.hbs

...

<body>

  {{{body}}}

  {{#each jsList}}

  <script src="{{this}}" charset="utf-8"></script>

  {{/each}}

</body>


//navigator.hbs

{{css '/stylesheets/components/navigator.css'}}

{{js '/javascripts/components/navigator.js'}}

<ul class="titles border" id="navigator">

  <li class="title">标题1</li>

  <li class="title">标题2</li>

  <li class="title">标题3</li>

</ul>


现在已经实现将css、js、html封装成独立的组件了,不过这样还是有个问题。如果a组件引用了public.css和a.css文件,而b组件引用了public.css和b.css文件,那么按照上面的做法,会在head写两个同样的link标签,组件共同依赖的文件越多,重复的标签就越多。这当然不是我们所想看到的。


实现步骤4:依赖


为了解决这个问题我们还需要去重复,对刚才的两个helper改造一下即可。


//app.js

hbs.registerHelper('css', function(str, option) {

  var cssList = this.cssList || [];

  if(cssList.indexOf(str)<0) {

    cssList.push(str);

  }

  this.cssList = cssList.concat();

});

hbs.registerHelper('js', function(str, option) {

  var jsList = this.jsList || [];

  if(jsList.indexOf(str)<0) {

    jsList.push(str);

  }

  this.jsList = jsList.concat();

});


实现步骤5:合并


最后一步优化。为了减少请求,可以将不同组件所需的资源文件进行合并。这里推荐一个插件loader。


示例代码下载地址(https://github.com/yalishizhude/back-componentization-example)


以上就是关于后端利用模板实现组件化的探索,实现上虽无问题,但是由于目前没有新的项目,无法在实际项目中使用,所以不知道会不会带来其它的“坑”。欢迎有兴趣的朋友使用后与我交流~



专栏作者简介 ( 点击 → 加入专栏作者 


亚里士朱德:web前端工程师,jsp工程师。擅长PC端网站开发,移动端微信开发。热爱node.js、mongoDB等后端技术。

打赏支持作者写出更多好文章,谢谢!



【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!



 
前端大全 更多文章 详解Javascript中的Object对象 结合个人经历总结的前端入门方法 前端不为人知的一面–前端冷知识集锦 一份优秀的前端开发工程师简历是怎么样的? 浅谈Web缓存
猜您喜欢 数据库设计 step by step(一) 聊聊代码规范 【超详解PPT】元数据驱动的微服务架构(下) 【原译】自文档化的JavaScript代码的开发方法 一乐:当你手抖删了线上数据库..