JavaScript模板引擎实现原理实例详解
本文实例讲述了JavaScript模板引擎实现原理。分享给大家供大家参考,具体如下:
1、入门实例
首先我们来看一个简单模板:
{{title}}
其中被{{xxx}}包含的就是我们要替换的变量。
接着我们可能通过ajax或者其他方法获得数据。这里我们自己定义了数据,具体如下:
vardata=[
{
title:"CreateaStickyNoteEffectin5EasyStepswithCSS3andHTML5",
href:"http://net.tutsplus.com/tutorials/html-css-techniques/create-a-sticky-note-effect-in-5-easy-steps-with-css3-and-html5/",
imgSrc:"https://d2o0t5hpnwv4c1.cloudfront.net/771_sticky/sticky_notes.jpg"
},
{
title:"Nettuts+Quiz#8",
href:"http://net.tutsplus.com/articles/quizzes/nettuts-quiz-8-abbreviations-darth-sidious-edition/",
imgSrc:"https://d2o0t5hpnwv4c1.cloudfront.net/989_quiz2jquerybasics/quiz.jpg"
}
];
ok,现在的问题就是我们怎么把数据导入到模板里面呢?
第一种大家会想到的就是采用replace直接替换里面的变量:
template=document.querySelector('#template').innerHTML,
result=document.querySelector('.result'),
i=0,len=data.length,
fragment='';
for(;i
第二种的话,相对第一种比较灵活,采用的是正则替换,对于初级前端,很多人对正则掌握的并不是很好,一般也用的比较少。具体实现如下:
template=document.querySelector('#template').innerHTML,
result=document.querySelector('.result'),
attachTemplateToData;
//将模板和数据作为参数,通过数据里所有的项将值替换到模板的标签上(注意不是遍历模板标签,因为标签可能不在数据里存在)。
attachTemplateToData=function(template,data){
vari=0,
len=data.length,
fragment='';
//遍历数据集合里的每一个项,做相应的替换
functionreplace(obj){
vart,key,reg;
//遍历该数据项下所有的属性,将该属性作为key值来查找标签,然后替换
for(keyinobj){
reg=newRegExp('{{'+key+'}}','ig');
t=(t||template).replace(reg,obj[key]);
}
returnt;
}
for(;i
与第一种相比较,第二种代码看上去多了,但是功能实则更为强大了。第一种我们需要每次重新编写变量名,如果变量名比较多的话,会比较麻烦,且容易出错。第二种的就没有这些烦恼。
2、模板引擎相关知识
通过上面的例子,大家对模板引擎应该有个初步的认识了,下面我们来讲解一些相关知识。
2.1模板存放
模板一般都是放置到textarea/input等表单控件,或者script等标签中。比如上面的例子,我们就是放在script标签上的。
2.2模板获取
一般都是通过ID来获取,document.getElementById("ID"):
//textarea或input则取value,其它情况取innerHTML
varhtml=/^(textarea|input)$/i.test(element.nodeName)?element.value:element.innerHTML;
上面的是通用的模板获取方法,这样不管你是放在textarea/input还是script标签下都可以获取到。
2.3模板函数
一般都是templateFun("id",data);其中id为存放模板字符串的元素id,data为需要装载的数据。
2.4模板解析编译
模板解析主要是指将模板中JavaScript语句和html分离出来,编译的话将模板字符串编译成最终的模板。上面的例子比较简单,还没有涉及到模板引擎的核心。
2.5模板分隔符
要指出的是,不同的模板引擎所用的分隔符可能是不一样,上面的例子用的是{{}},而Jquerytmpl使用的是<% %>。
3、jQuerytmpl实现原理解析
jQuerytmpl是由jQuery的作者写的,代码短小精悍。总共20多行,功能却比我们上面的强大很多。我们先来看一看源码:
(function(){
varcache={};
this.tmpl=functiontmpl(str,data){
varfn=!/\W/.test(str)?
cache[str]=cache[str]||
tmpl(document.getElementById(str).innerHTML):
newFunction("obj",
"varp=[],print=function(){p.push.apply(p,arguments);};"+
"with(obj){p.push('"+
str
.replace(/[\r\t\n]/g,"")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g,"$1\r")
.replace(/\t=(.*?)%>/g,"',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+"');}returnp.join('');");
returndata?fn(data):fn;
};
})();
初看是不是觉得有点懵,完全不能理解的代码。没事,后面我们会对源码进行解释的,我们还是先看一下所用的模板
<%for(vari=0;i
"rel="externalnofollow"><%=users[i].name%>
<%}%>
可以发现,这个模板比入门例子的模板更为复杂,因为里面还夹杂着JavaScript代码。JavaScript代码采用<% %>包含。而要替换的变量则是用<%= %>分隔开的。
下面我再来对代码做个注释。不过即使看了注释,你也不一定能很快理解,最好的办法是自己实际动手操作一遍。
//代码整个放在一个立即执行函数里面
(function(){
//用来缓存,有时候一个模板要用多次,这时候,我们直接用缓存就会很方便
varcache={};
//tmpl绑定在this上,这里的this值得是window
this.tmpl=functiontmpl(str,data){
//只有模板才有非字母数字字符,用来判断传入的是模板id还是模板字符串,
//如果是id的话,判断是否有缓存,没有缓存的话调用tmpl;
//如果是模板的话,就调用newFunction()解析编译
varfn=!/\W/.test(str)?
cache[str]=cache[str]||
tmpl(document.getElementById(str).innerHTML):
newFunction("obj",
//注意这里整个是字符串,通过+号拼接
"varp=[],print=function(){p.push.apply(p,arguments);};"+
"with(obj){p.push('"+
str
//去除换行制表符\t\n\r
.replace(/[\r\t\n]/g,"")
//将左分隔符变成\t
.split("<%").join("\t")
//去掉模板中单引号的干扰
.replace(/((^|%>)[^\t]*)'/g,"$1\r")
//为html中的变量变成",xxx,"的形式,如:\t=users[i].url%>变成',users[i].url,'
//注意这里只有一个单引号,还不配对
.replace(/\t=(.*?)%>/g,"',$1,'")
//这时候,只有JavaScript语句前面才有"\t",将\t变成');
//这样就可把html标签添加到数组p中,而javascript语句不需要push到里面。
.split("\t").join("');")
//这时候,只有JavaScript语句后面才有"%>",将%>变成p.push('
//上一步我们再html标签后加了');,所以要把p.push('语句放在html标签放在前面,这样就可以变成JavaScript语句
.split("%>").join("p.push('")
//将上面可能出现的干扰的单引号进行转义
.split("\r").join("\\'")
//将数组p变成字符串。
+"');}returnp.join('');");
returndata?fn(data):fn;
};
})();
上面代码中,有一个要指出的就是newFunction的使用方法。给newFunction()传一个字符串作为函数的body来构造一个JavaScript函数。编程中并不经常用到,但有时候应该是很有用的。
下面是newFunction的基本用法:
//最后一个参数是函数的body(函数体),类型为string;
//前面的参数都是索要构造的函数的参数(名字)
varmyFunction=newFunction('users','salary','returnusers*salary');
最后的字符串就是下面这种形式:
varp=[],
print=function(){
p.push.apply(p,arguments);
};
with(obj){
p.push('');
for(vari=0;i',users[i].name,'');
}
p.push('
');
}
returnp.join('');
里面的print函数在我们的模板里面是没有用到的。
要指出的是,采用push的方法在IE6-8的浏览器下会比+=的形式快,但是在现在的浏览器里面,+=是拼接字符串最快的方法。实测表明现代浏览器使用+=会比数组push方法快,而在v8引擎中,使用+=方式比数组拼接快4.7倍。所以目前有些更高级的模板引擎会根据javascript引擎特性采用了两种不同的字符串拼接方式。
下面的代码是摘自腾讯的artTemplate的,根据浏览器的类型来选择不同的拼接方式。功能越强大,所考虑的问题也会更多。
varisNewEngine=''.trim;//'__proto__'in{}
varreplaces=isNewEngine
?["$out='';","$out+=",";","$out"]
:["$out=[];","$out.push(",");","$out.join('')"];
挑战:有兴趣的可以改用+=来实现上面的代码。
总结
模板引擎原理总结起来就是:先获取html中对应的id下得innerHTML,利用开始标签和关闭标签进行字符串切分,其实是将模板划分成两部份内容,一部分是html部分,一部分是逻辑部分,通过区别一些特殊符号比如each、if等来将字符串拼接成函数式的字符串,将两部分各自经过处理后,再次拼接到一起,最后将拼接好的字符串采用newFunction()的方式转化成所需要的函数。
目前模板引擎的种类繁多,功能也越来越强大,不同模板间实现原理大同小异,各有优缺,请按需选择。
参考文章:
1、QuickTip:CreateaMakeshiftJavaScriptTemplatingSolution
2、JavaScript模板引擎的应用场景及实现原理
3、JavaScript构建自己的模板小引擎
更多关于JavaScript相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。