前端模版原理&实现(一)

【简单介绍】

目前流行的react,angular,vue等MV*框架,都离不开模版。目前比较流程的前端模版引擎,除了这几个框架本身的模版以外,还有:

  https://github.com/janl/mustache.js 基于JavaScript的Logic-less(无逻辑或轻逻辑)模板。

  https://github.com/twitter/hogan.js  上面的优化版,twitter出品

  https://github.com/wycats/handlebars.js 完全兼容mustcache的语法

  https://github.com/paularmstrong/swig  拥有更强悍的模板继承与block 重写功能

  最不推荐的是jade,有点华而不实,过度设计,导致页面工作量大,性能差。

  虚拟DOM时代流行的jsx就是无逻辑模版,之所以流行无逻辑模版或者轻逻辑模版,其主要原因是改动成本比较少。

对于模版来说,最简单而言,就是将某个可变数据放到适当的地方(填空),其次,可以控制这个区域输出不输入(if指令),或让其某个区域循环输入多次(for指令),更甚至,实现模版互相嵌套(layout与block)。

  实现if和for有两种方法:

    1、单纯的区域,插入一个js语句,里面有if语句或for语句。

    2、语法糖,比如说v-for,v-if。语法糖的用法比直接引用js语句简单,但是带来学习成本与拓展功能。

  每个模版的if,for指令语法都不一样,并且你想循环做一些处理,比如过滤一些数据,或突然在某处中断,这又得引用一些新的语句。随着模版要求前后共用,就有了传输成本,直接写js语句在模版里肯定比不过魔法糖。因此基于种种原因,mustache风格的模板就成为主流。

现在语法有三种:

  PHP/ASP/JSP风格:

<% if ( list.length ) { %>
  <ol>
    <% for ( n=0; n<list.length; ++n ) { %>
      <li>
        <%= list[n] %>
      </li>
    <% } %>
  </ol>
<% } %>

mustcache风格,高级语法有限,通常难自定义拓展:

{{#if list.length}}
  <ol>
    {{#each list item}}
      <li>
        {{ item }}
      </li>
    {{/each}}
  </ol>
{{/if}}

属性绑定风格:

<ol v-if="list.length">
  <li v-for="item in list">
      {{item}}
  </li>
</ol>

前两者只能出现于script、textarea等容器内部。因此<分隔符与标签的<容器容易造成冲突,并且也不利于IDE的格式化处理。

属性绑定风格则是MVVM时期最流行的模版定义风格,某页面某个区域就是一个模版,不需要进行append等操作。

【简单模版

有时候,我们不需要太强大的javascript模版引擎(比如handlebarsjs),我们只是需要简单的模版里绑定一些非常简单的字段,示例如下:

首先我们先来定义我们需要的模版,在id为template的script块里:

 1 <!doctype>
 2 <html>
 3 <head>
 4     <meta charset=utf-8>
 5     <title>Simple Templating</title>
 6 </head>
 7 <body>
 8    
 9   <div class="result"></div>
10    
11   <script type="template" id="template">
12     <h2> 
13       <a href="{{href}}">
14         {{title}} 
15       </a>
16     </h2>
17     <img src="{{imgSrc}}" alt="{{title}}">
18   </script>
19  
20 </body>
21 </html>

然后我们通过ajax拿到了数据格式如下:

var data =[
    {
title: "Knockout应用开发指南",
href: "http://www.cnblogs.com/TomXu/archive/2011/11/21/2257154.html",
imgSrc: "http://images.cnblogs.com/cnblogs_com/TomXu/339203/o_knockout2.jpg"
},
{
title: "微软ASP.NET站点部署指南",
href: "http://www.cnblogs.com/TomXu/archive/2011/11/25/2263050.html",
imgSrc: "http://images.cnblogs.com/cnblogs_com/TomXu/339203/o_vs.jpg"
},
{
title: "HTML5学习笔记简明版",
href: "http://www.cnblogs.com/TomXu/archive/2011/12/06/2277499.html",
imgSrc: "http://images.cnblogs.com/cnblogs_com/TomXu/339203/o_LearningHtml5.png"
}
]

我们有两种方式来绑定数据

1、简单的hardcode的方式,不灵活,不可取

var template = document.querySelector('#template').innerHTML,
result = document.querySelector('.result'),
i = 0, len = data.length, 
fragment = '';
  for ( ; i < len; i++ ) {
    fragment += template
      .replace( /\{\{title\}\}/, data[i].title )
      .replace( /\{\{href\}\}/, data[i].href )
      .replace( /\{\{imgSrc\}\}/, data[i].imgSrc );  
  }
  result.innerHTML = fragment;

2、比较灵活,通过正则表达式来替换所有花括号的值,而无需一个一个的替换,相对比较灵活,但是要注意模版标签可能在数据里不存在的情况:

var template = document.querySelector('#template').innerHTML,
result = document.querySelector('.result'),
attachTemplateToData;
// 将模板和数据作为参数,通过数据里所有的项将值替换到模板的标签上(注意不是遍历模板标签,因为标签可能不在数据里存在)。
attachTemplateToData = function(template, data) {
        var i = 0,
            len = data.length,
            fragment = '';
        // 遍历数据集合里的每一个项,做相应的替换
        function replace(obj) {
            var t, key, reg;
       //遍历该数据项下所有的属性,将该属性作为key值来查找标签,然后替换
            for (key in obj) {
                reg = new RegExp('{{' + key + '}}', 'ig');
                t = (t || template).replace(reg, obj[key]);
            }
            return t;
        }
        for (; i < len; i++) {
            fragment += replace(data[i]);
        }
        return fragment;
    };
result.innerHTML = attachTemplateToData(template, data);

参照原文:

https://segmentfault.com/a/1190000006990480

http://www.cnblogs.com/TomXu/archive/2011/12/15/2284752.html

 

posted @ 2018-07-20 18:24  暖暖的风儿  阅读(823)  评论(0)    收藏  举报