下面是小C的js模版引擎v6,写得非常精巧。
先看一下怎么使用吧。
var tpl = Template(options, helper).render();
html模版,分隔符分别是<&和&>,@会被解析为"data.",就是你的数据对象,避免了使用with。
<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>
下面是引擎的源代码,就让我来为你详细讲解吧
先找到入口,render方法就是整个引擎的入口
//闭包,w是window对象
(function(w){
//定义Template对象,使其单例
w.Template=Template||{};
function Template(options,helper){
//将this对象指向Template对象 ,并初始化
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;
//?: 不分组 ?= 紧跟者
//按照分隔符分开,如果遇到分隔符是{{,}}的情况,{{{44}}},会先被分为"{{{44} }}",最后被分割为["{","{{44}", "}}"]
//例如:{{}}} ->["{{}", "}}"] {{{->["{","{{"]
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+')'))
}
//过滤右分隔符,["{","{{44}", "}}"] -> ["{","{{44}"]
temp.filter(function(k,v){
return !(new RegExp(self.right)).test(v);
}).each(
function(k,v){ //核心代码
//分3中情况
//<&= title() '^'+self.left+'\s*=' temp.push(title()) 放到temp中
//<& for(var i=0,tl = @trs.length,tr;i<tl;i++){ &> 直接放到self.body中
//<table border=1> 放到temp中
if((new RegExp('^'+self.left)).test(v)){
v=v.replace(/@/g,'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');
}
})
//返回方法体,temp中的才是最终的html代码
return self.body.join("");
};
Template.prototype={
//初始化
init:function(options,helper){
this.tpl=options.tpl; //html模版
this.left=options.left||"{{"; //左分隔符,本例中就是<&
this.right=options.right||"}}"; //左分隔符,本例中就是&>
this.body=[]; //主要用来存放模版的主要方法体
this.compiled=null; //定义compiled方法
this.data=options.data; //数据
this.helper=helper; //helper对象
},
compile:function(){ //编译模版,将模版解析为一个function
if(!this.compiled){ //已经缓存就不需要在编译
var helper=[];
if(this.helper){ //将helper对象解析为var <key> = <value> 的变量定义形式
for(var h in this.helper){
helper.push('var '+h+'=this.helper["'+h+'"]');
}
}
//将整个模版转化为一个function,此处是整个引擎的实现原理
/**
* 本例中的模版解析后的方法是
* function(data){
//helper对象
var title = function(){
return "<p>这是使用视图helper输出的代码片断</p>"
}
var temp=[];
temp.push(title());
temp.push('<table border=1>');
for(var i=0,tl = data.trs.length,tr;i<tl;i++){
tr = data.trs[i];
temp.push('<tr>\r\n<td>');
temp.push(tr.name);
temp.push('</td> <td>');
temp.push(tr.age);
temp.push('</td> <td>');
temp.push(tr.sex || '男');
temp.push('</td>\r\n</tr>');
}
return temp.join("");
}
*
*/
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;
}
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;
}