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
'); } returnp.join('');',users[i].name,''); } p.push('
里面的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程序设计有所帮助。