上次的js模板引擎v4基本已经可以满足大部分需求了,期间也碰到过一些问题,当模板里需要一些函数来处理数据时,必须得定一个全局函数,或者在一个全局变量上挂着一个属性,这种用法还是感觉蛮不爽的,
没必要为了一个只在模板内部使用的工具函数影响其外部的代码,所以这次模板引入了像smarty模板那样可以定义在模版内部定义函数的helper机制,同时改善了v4中所以模板数据前面必须得加个data,
比如有个对象a,模板里引用时必须得写上data.a这样不爽的东西,采用$a代替data.a用着的感觉好多了。
该模板优点:
1.模板采用js语法,没有学习成本
2.也是由于优点1所以该模板的解析速度还是很占优势的
3.可以自定义模板分隔符,就算是与js语法冲突的{{ }}都没有问题,避免了和后端模板分隔符冲突的风险
4.引入helper机制,避免影响全局变量
//helper机制的使用
var tpl = Template(options, helper);
title: function(){
return "<p>这是使用视图helper输出的代码片断</p>"
},
handle:function(data){
return data.name.replace("aaa","bbb");
}
}
(function(w){
var quote=function (str) {
str = str.replace(/[\x00-\x1f\\]/g, function (chr) {
var special = metaObject[chr];
return special ? special : '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4)
});
return str.replace(/"/g, '\\"') ;
},
metaObject = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'\\': '\\\\'
};
w.Template=Template||{};
function Template(options,helper){
return this instanceof arguments.callee?this.init(options,helper):new arguments.callee(options,helper);
}
Template.parse=function(self){
var temp;
if(self.right=="}}"){//这里主要是为了解决{{{造成的bug!
temp=self.tpl.replace(/(}})([^}])/g,function(){
return arguments[1]+" "+arguments[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').replace(/\\n/g,''));
}else{
self.body.push(v.replace(new RegExp('^'+self.left+'\s*(.*)'),'$1\n').replace(/\\n/g,''));
}
}
else {self.body.push('\ttemp.push(\"'+v.replace(/\n|\t/g,'')+'\");\n');}
})
return self.body.join("");
};
Template.prototype={
init:function(options,helper){
this.tpl=quote(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+'"]'); //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.compile()(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 | 女 |
function anonymous(data) { var title = this.helper.title; var handle = this.helper.handle; var temp = []; temp.push("\n <div>\n\t "); temp.push(title()); temp.push("\n\t \n\t "); temp.push(handle(data.a)); temp.push("\n<h2>\u5BF9\u8C61\u904D\u5386</h2>\n "); for (var i in data.a) { temp.push("\n <li>"); temp.push(i); temp.push(":"); temp.push(data.a[i]); temp.push("</li>\n "); } temp.push("\n\n\t "); if (data.b == 100) { temp.push("\n\t b\u7684\u503C\u4E3A:"); temp.push(data.b); temp.push("\n\t "); } else { temp.push("\n\t b\u7684\u503C\u4E0D\u6EE1\u8DB3if\u6761\u4EF6\n\t "); } temp.push("\n\n\t <table style=\"text-align:center;\" >\n\t <tr><th width='200;'>\u6B4C\u66F2\u540D</th><th width='200;'>\u6B4C\u624B</th><th width='200'>\u8FDB\u5165\u8BD5\u542C</th></tr>\n\t "); for (var i = 0, l = data.song.length; i < l; i++) { temp.push("\n\t <tr><td>"); temp.push(data.song[i].songname); temp.push("</td><td>"); temp.push(data.song[i].singer); temp.push("</td><td><a href='"); temp.push(data.song[i].url); temp.push("' >"); temp.push(data.song[i].url); temp.push("</a></td></tr>\n\t "); } temp.push("\n\t </table>\n\n "); for (var i = 0, l = data.url.length; i < l; i++) { temp.push("\n\t <img src='"); temp.push(data.url[i]); temp.push("'/><br/>\n "); } temp.push("\n </div>\n"); return temp.join(""); }
之前生成的匿名函数为
function anonymous(data) { var title = function () {return "<p>\u8FD9\u662F\u4F7F\u7528\u89C6\u56FEhelper\u8F93\u51FA\u7684\u4EE3\u7801\u7247\u65AD</p>";}; var handle = function (data) {return data.name + "@cnblogs!";}; var temp = []; temp.push("\n <div>\n\t "); temp.push(title()); temp.push("\n\t \n\t "); temp.push(handle(data.a)); temp.push("\n<h2>\u5BF9\u8C61\u904D\u5386</h2>\n "); for (var i in data.a) { temp.push("\n <li>"); temp.push(i); temp.push(":"); temp.push(data.a[i]); temp.push("</li>\n "); } temp.push("\n\n\t "); if (data.b == 100) { temp.push("\n\t b\u7684\u503C\u4E3A:"); temp.push(data.b); temp.push("\n\t "); } else { temp.push("\n\t b\u7684\u503C\u4E0D\u6EE1\u8DB3if\u6761\u4EF6\n\t "); } temp.push("\n\n\t <table style=\"text-align:center;\" >\n\t <tr><th width='200;'>\u6B4C\u66F2\u540D</th><th width='200;'>\u6B4C\u624B</th><th width='200'>\u8FDB\u5165\u8BD5\u542C</th></tr>\n\t "); for (var i = 0, l = data.song.length; i < l; i++) { temp.push("\n\t <tr><td>"); temp.push(data.song[i].songname); temp.push("</td><td>"); temp.push(data.song[i].singer); temp.push("</td><td><a href='"); temp.push(data.song[i].url); temp.push("' >"); temp.push(data.song[i].url); temp.push("</a></td></tr>\n\t "); } temp.push("\n\t </table>\n\n "); for (var i = 0, l = data.url.length; i < l; i++) { temp.push("\n\t <img src='"); temp.push(data.url[i]); temp.push("'/><br/>\n "); } temp.push("\n </div>\n"); return temp.join(""); }
可以看出当helper的函数较庞大时生成的这个匿名函数是多么恐怖,而且helper里的函数本来就是定义好的,干嘛不直接调用呢,还用new Function再次动态生成