利用 handlebars 实现后端组件化

作者: 前端  发布:2019-11-08

利用 handlebars 实现后端组件化

2016/04/27 · 基础技术 · handlebars, 组件化

本文作者: 伯乐在线 - 亚里士朱德 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

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

XHTML

/* 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 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>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ";
<html xmlns=";
<head>
    <title>YanCms左侧导航栏</title>
    <script src="js/jquery-1.4.2.min.js" type="text/javascript"></script>
    <script src="js/jquery-ui-1.8.custom.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            //#title代表页面中点击的元素
            $(".title").click(function () {
                var con = $(this).html();
                //#getTitle代表页面中想要得到title的值元素
                $("#getTitle").html(con);
                //$(this).attr('href')获取超链接
                var con1 = $(this).attr('href');
               // $("#getTitle").attr("href", "con1");
               // alert(con1);
            });
        });
   </script>
    <script type="text/javascript">
        jQuery().ready(function () {
            jQuery("#navigation").accordion({
                header: '.head',
                event: 'click',
                fillSpace: true,
                animated: 'bounceslide'
            });
        });
 </script>
    <link href="css/left.css" rel="stylesheet" type="text/css" />
 </head>
<body style="background-color:#e6f4fa;" >
        <div class="subMenuTitle" id="getTitle"><a href="#">主功能菜单</a></div>
  <div style="height:100%;">
   <ul id="navigation">
    <li>
     <a class="head">类别管理</a>
     <ul>
      <li class="title">
       <a href="#1233333" >博文列表</a>
      </li>
      
      <li class="title">
       <a href="#">添加博文</a>
      </li>
     </ul>
    </li>
    <li>
     <a class="head">类别管理</a>
     <ul>
      <li>
       <a href="#" target="rightFrame"> 类别列表</a>
      </li>
        </ul>
    </li>
    <li>
     <a class="head">友情链接</a>
     <ul>
      <li>
       <a href="#" target="rightFrame"> 链接列表</a>
      </li>
      <li>
       <a href="#" target="rightFrame">添加链接</a>
      </li>
      
     </ul>
    </li>
    <li>
     <a class="head">系统管理</a>
     <ul>
      <li>
       <a href="#" target="rightFrame">修改管理员密码</a>
      </li>
      
     
     </ul>
    </li>
    
    <li>
     <a class="head">权限管理</a>
     <ul>
      <li>
       <a href="#" target="rightFrame">暂未开放</a>
      </li>
      <li>
       <a href="#" target="rightFrame">暂未开放</a>
      </li>
      
     
     </ul>
     
    </li>
   </ul>
  </div>
      
 </body>
</html>

实现步骤4:依赖

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

JavaScript

//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(); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//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();
});

 

实现步骤1:模板

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

XHTML

<ul class="titles border" id="navigator"> <li class="title">标题1</li> <li class="title">标题2</li> <li class="title">标题3</li> </ul>

1
2
3
4
5
<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为例:

XHTML

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

1
2
3
4
5
6
7
8
9
//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来实现一下“组件化”。

html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN html xmlns= head titleYanCms左侧...

实现步骤2:打包

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

  1. 在分页中加入link标签来引入所需的样式,想一想html代码中到处穿插link标签是什么感觉~且不说生成页面难以维护,浏览器渲染速度也会受影响。
  2. 把分页所需的样式放在公共的样式文件中,这是目前我们项目的通用做法,纯粹的懒人策略,缺点很明显,很多页面引用了一些无用的样式,浪费网络带宽,尤其当项目变大时这个缺点将更加明显。 所以最好的解决方法是按需加载,只加载引用组件所需的样式,当然样式文件按分页拆分得这么细的话会增加请求数,影响不会太大,如果想优化的话也可以压缩合并成一个请求,这个后面再说。 handlerbas中常见的扩展方式就是编写helper,我们可以编写一个helper,

XHTML

//app.js hbs.registerHelper('css', function(str, option) { //在上下文中创建一个数组用来保存该页面需要用到的css文件 this.cssList = this.cssList || []; this.cssList.push(str); });

1
2
3
4
5
6
//app.js
hbs.registerHelper('css', function(str, option) {
  //在上下文中创建一个数组用来保存该页面需要用到的css文件
  this.cssList = this.cssList || [];  
  this.cssList.push(str);
});

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

XHTML

//layout.hbs <head> <title>{{title}}</title> {{#each cssList}} <link rel="stylesheet" href="{{this}}" media="screen" title="no title" charset="utf-8"> {{/each}} </head> ...

1
2
3
4
5
6
7
8
9
//layout.hbs
<head>
  <title>{{title}}</title>
 
  {{#each cssList}}
  <link rel="stylesheet" href="{{this}}" media="screen" title="no title" charset="utf-8">
  {{/each}}
</head>
...

同时原来的分页改成

XHTML

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

1
2
3
4
5
6
7
//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逻辑来实现了。

print?<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "; 
<html xmlns="; 
<head> 
    <title>YanCms左侧导航栏</title> 
    <script src="js/jquery-1.4.2.min.js" type="text/javascript"></script> 
    <script src="js/jquery-ui-1.8.custom.min.js" type="text/javascript"></script> 
    <script type="text/javascript"> 
        $(document).ready(function () { 
            //#title代表页面中点击的元素 
            $(".title").click(function () { 
                var con = $(this).html(); 
                //#getTitle代表页面中想要得到title的值元素 
                $("#getTitle").html(con); 
                //$(this).attr('href')获取超链接 
                var con1 = $(this).attr('href'); 
               // $("#getTitle").attr("href", "con1"); 
               // alert(con1); 
            }); 
        }); 
   </script> 
    <script type="text/javascript"> 
        jQuery().ready(function () { 
            jQuery("#navigation").accordion({ 
                header: '.head', 
                event: 'click', 
                fillSpace: true, 
9159.com,                animated: 'bounceslide' 
            }); 
        }); 
    </script> 
    <link href="css/left.css" rel="stylesheet" type="text/css" /> 
    </head> 
<body style="background-color:#e6f4fa;" > 
        <div class="subMenuTitle" id="getTitle"><a href="#">主功能菜单</a></div> 
        <div style="height:100%;"> 
            <ul id="navigation"> 
                <li> 
                    <a class="head">类别管理</a> 
                    <ul> 
                        <li class="title"> 
                            <a href="#1233333" >博文列表</a> 
                        </li> 
                         
                        <li class="title"> 
                            <a href="#">添加博文</a> 
                        </li> 
                    </ul> 
                </li> 
                <li> 
                    <a class="head">类别管理</a> 
                    <ul> 
                        <li> 
                            <a href="#" target="rightFrame"> 类别列表</a> 
                        </li> 
                    </ul> 
                </li> 
                <li> 
                    <a class="head">友情链接</a> 
                    <ul> 
                        <li> 
                            <a href="#" target="rightFrame"> 链接列表</a> 
                        </li> 
                        <li> 
                            <a href="#" target="rightFrame">添加链接</a> 
                        </li> 
                         
                    </ul> 
                </li> 
                <li> 
                    <a class="head">系统管理</a> 
                    <ul> 
                        <li> 
                            <a href="#" target="rightFrame">修改管理员密码</a> 
                        </li> 
                         
                     
                    </ul> 
                </li> 
                 
                <li> 
                    <a class="head">权限管理</a> 
                    <ul> 
                        <li> 
                            <a href="#" target="rightFrame">暂未开放</a> 
                        </li> 
                        <li> 
                            <a href="#" target="rightFrame">暂未开放</a> 
                        </li> 
                         
                     
                    </ul> 
                     
                </li> 
            </ul> 
        </div> 
        
    </body> 
</html> 

实现步骤3:逻辑

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

JavaScript

//app.js hbs.registerHelper('js', function(str, option) { this.jsList = this.jsList || []; this.jsList.push(str); });

1
2
3
4
5
//app.js
hbs.registerHelper('js', function(str, option) {
  this.jsList = this.jsList || [];
  this.jsList.push(str);
});

XHTML

//layout.hbs ... <body> {{{body}}} {{#each jsList}} <script src="{{this}}" charset="utf-8"></script> {{/each}} </body>

1
2
3
4
5
6
7
8
//layout.hbs
...
<body>
  {{{body}}}
  {{#each jsList}}
  <script src="{{this}}" charset="utf-8"></script>
  {{/each}}
</body>

XHTML

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

1
2
3
4
5
6
7
8
//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标签,组件共同依赖的文件越多,重复的标签就越多。这当然不是我们所想看到的。

实现步骤5:合并

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

示例代码下载地址

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

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

打赏作者

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

任选一种支付方式

9159.com 1 9159.com 2

1 赞 收藏 评论

关于作者:亚里士朱德

9159.com 3

微信公众号“web学习社”;js全栈工程师,熟悉node.js、mongoDB。开发者头条top10专栏作者慕课网签约讲师个人博客:yalishizhude.github.io 个人主页 · 我的文章 · 19 ·     

9159.com 4

本文由9159.com发布于前端,转载请注明出处:利用 handlebars 实现后端组件化

关键词:

上一篇:没有了
下一篇:没有了