js模板引擎v6

 

这次的模板引擎主要在算法方面做了些改进,主要借鉴正美的思路,

https://github.com/RubyLouvre/mass-Framework/issues/22

传统的字符串模块都要处理转义的问题,但我这新方法通过传参手段消灭了这步骤

核心原理

function a(){
      var b = ["1,2,3"]
      return Function("b", "function test(){}; return function(data){ test(); return  b }")(b)
}
a()+""

主要原理是根据模版生成的字符串构造函数对字符串的转义要求很高,一不留神就会出错,这次采用了将需要转义的字符串存入闭包中来进行引用,这样就很好的避免字符串转义的操作。
同时也极大的减少了最后new Function时的函数体。

下面这段代码体现出核心原理
self.__lines[i]=v;
self.body.push('\ttemp.push(this.__lines['+(i++)+']);\n');
之前的版本是这样的
self.body.push('\ttemp.push(\"'+v.replace(/\n|\t/g,'')+'\");\n');那么变量v必须得转义一次了(此步操作之前就得转义)。

  该模板优点:

    1.模板采用js语法,没有学习成本

    2.也是由于优点1所以该模板的解析速度还是很占优势的

    3.可以自定义模板分隔符,就算是与js语法冲突的{{ }}都没有问题,避免了和后端模板分隔符冲突的风险

    4.引入helper机制,避免影响全局变量

      5.内置缓存,每个模板只会编译一次,生成的模板函数会对其缓存

   //helper机制的使用
   var tpl = Template(options, helper);
  options是一个对象,有以下属性
   tpl 必选 待解析的模板字符串
   left 可选 左分隔符 默认 {{
  right 可选 右分隔符 默认}}
  data 可选 要渲染的数据  如果在这个地方不传入的话  可以在调用tpl.render时传入
 
  helper是一个对象,里边必须为函数
  比如
         {
            title: function(){
                return "<p>这是使用视图helper输出的代码片断</p>"
            },
            handle:function(data){
              return data.name.replace("aaa","bbb");
            }
        }
 
以下是该模板引擎核心代码
(function(w){
    w.Template=Template||{};
    function Template(options,helper){
        return this instanceof Template?this.init(options,helper):new Template(options,helper);
    }
    Template.parse=function(self){
	       if(!self.__lines){
	         self.__lines=[];
	       }
           var temp,i=0;
           if(self.right=="}}"){//这里是为了解决}}}造成的bug!
              temp=self.tpl.replace(/(}})([^}])/g,"$1 $2").split(new RegExp('(?='+self.left+')|('+self.right+')(?:[^}])'))
           }else{
              temp=self.tpl.split(new RegExp('(?='+self.left+')|('+self.right+')'))
           }
            temp.filter(function(k,v){
                   return !(new RegExp(self.right)).test(v);
            }).each(
              function(k,v){
                if((new RegExp('^'+self.left)).test(v)){
                    v=v.replace('@','data.');
                    if(new RegExp('^'+self.left+'\s*=').test(v)){
                       self.body.push(v.replace(new RegExp('^'+self.left+'\s*=(.*)'),'\ttemp.push($1);\n'));
                    }else{
                       self.body.push(v.replace(new RegExp('^'+self.left+'\s*(.*)'),'$1\n'));
                    }
                }
                else {
				  self.__lines[i]=v;
			      self.body.push('\ttemp.push(this.__lines['+(i++)+']);\n');
				}
              })
              return self.body.join("");
        };
    Template.prototype={
        init:function(options,helper){
            this.tpl=options.tpl;
            this.left=options.left||"{{";
            this.right=options.right||"}}";
            this.body=[];
            this.compiled=null;
            this.data=options.data;
            this.helper=helper;
        },
        compile:function(){
            if(!this.compiled){
                var helper=[];
                if(this.helper){
                   for(var h in this.helper){
                     helper.push('var '+h+'=this.helper["'+h+'"]'); 
                   }
			    }
			    this.compiled=new Function("data",helper.join(";")+';var temp=[];\n'+Template.parse(this)+'\n return temp.join("");');
			}
		    return this.compiled;
        },
        render:function(data){
            return this.compile().call(this,data||this.data);
        }
    }
})(this);
 Array.prototype.filter=function(fn){
   var temp=[];
   for(var i=0,l=this.length;i<l;i++){
      this[i]&&fn.call(this,i,this[i])&&temp.push(this[i]);
   }
  return temp;
}
Array.prototype.each=function(fn){
   var temp=[];
   for(var i=0,l=this.length;i<l;i++){
     fn.call(this,i,this[i]);
   }
   return this;
}

 

下面是示例代码

<script type="tmpl" id="table_tmpl">
        <&= title() &>
        <table border=1>
        <& for(var i=0,tl = @trs.length,tr;i<tl;i++){  &>
            <& tr = @trs[i]; &>
            <tr>
            <td><&= tr.name&></td> <td><&= tr.age&></td> <td><&= tr.sex || '男' &></td>
            </tr>
            <& } &>
        </table>
        <img src="<&= @href &>">
</script>

 

<script>
           
    var trs = [
            {name:"隐形杀手",age:29,sex:"男"},
            {name:"索拉",age:22,sex:"男"},
            {name:"fesyo",age:23,sex:"女"},
            {name:"恋妖壶",age:18,sex:"男"},
            {name:"竜崎",age:25,sex:"男"},
            {name:"你不懂的",age:30,sex:"女"}
        ]
 
        var html = Template({
           tpl:document.getElementById("table_tmpl").text,
           left:"<&",
           right:"&>",
           data:{
            trs: trs,
            href: "http://images.cnblogs.com/cnblogs_com/rubylouvre/202906/o_type4.jpg"
          }
        },{
            title: function(){
                return "<p>这是使用视图helper输出的代码片断</p>"
           }
                    
        });
        document.getElementById("test123").innerHTML=html.render()
</script>

 

下面是输出结果

 

这是使用视图helper输出的代码片断

隐形杀手 29
索拉 22
fesyo 23
恋妖壶 18
竜崎 25
你不懂的 30
                                                                                                                                             
 
 
 注:改变helper实现机制,上边代码helper函数都放到new Function了,这样当helper函数很大时对性能影响很大,所以将其提出来了,
 
采用helpers.push('var '+h+'=this.helper["'+h+'"]');这种方式,然后调用render时传入this,return this.compile().call(this,data||this.data);
 
其实也没必要把已经存在的一个函数转化成字符串然后再重新生成。

生成的匿名函数代码
function anonymous(data) {
    var title = this.helper.title;
    var handle = this.helper.handle;
    var cut = this.helper.cut;
    var temp = [];
    temp.push(this.__lines[0]);
    temp.push(title());
    temp.push(this.__lines[1]);
    temp.push(handle(data.a));
    temp.push(this.__lines[2]);
    for (var i in data.a) {
        temp.push(this.__lines[3]);
        temp.push(i);
        temp.push(this.__lines[4]);
        temp.push(data.a[i]);
        temp.push(this.__lines[5]);
    }
    temp.push(this.__lines[6]);
    if (data.b == 100) {
        temp.push(this.__lines[7]);
        temp.push(data.b);
        temp.push(this.__lines[8]);
    } else {
        temp.push(this.__lines[9]);
    }
    temp.push(this.__lines[10]);
    for (var i = 0, l = data.song.length; i < l; i++) {
        temp.push(this.__lines[11]);
        temp.push(data.song[i].songname);
        temp.push(this.__lines[12]);
        temp.push(data.song[i].singer);
        temp.push(this.__lines[13]);
        temp.push(data.song[i].url);
        temp.push(this.__lines[14]);
        temp.push(cut(data.song[i].url));
        temp.push(this.__lines[15]);
    }
    temp.push(this.__lines[16]);
    for (var i = 0, l = data.url.length; i < l; i++) {
        temp.push(this.__lines[17]);
        temp.push(data.url[i]);
        temp.push(this.__lines[18]);
    }
    temp.push(this.__lines[19]);
    return temp.join("");
}

 




posted @ 2012-08-30 12:41  淘小杰  阅读(1888)  评论(3编辑  收藏  举报