`

Ext.template分析

 
阅读更多

说起模板,很多人都会想起FreeMaker。什么是模板呢?模板就是按预前给定的模样生产出来。这个预前给定的模样就是模板。在程序开发上的模板有一点不同,它不是完全一模一样的。


举个例子:比如我要在页面显示某人的一些信息

Java代码
  1. <divstyle=”….”>小王</div><divstyle=”….”>1983-09-24</div>

这是一段内容。很多时间我们需要变化的仅仅是name,birthday这两个部分。如果对于小王,小李等不同的人,每次都要完全重写这一段内容。首先很烦琐,修改起来也麻烦,style是统一的,如果只要修改一个就能统一修改多好啊。
于是就出现了模板,把静态不变的内容做成模板,把动态变化的内容采用插值(${})的形式插入。构成我们要的内容。说到这里,JSP就是一个模板。


为了统一名词,在这里把动态变化的内容称为插值。把静态不变的内容称为模板静态部分。把插值和静态部分构成的整体称为模板内容。把当模板内容中的插值采用了实际值时,我们就称为模板生成内容。


现在开始谈论Ext.Template吧。 对于Javascript模板,其最终的结果就是生成在浏览器中能显示的内容。对于显示的内容来说,首先会想法是格式。模板静态部分可以写死格式,这个插 值部分怎么办呢?比如比如出生年月不想采用1983-09-24而是采用1983年9月24日。对于这样的格式转换,每种Lib都会有通用的函数。现在的 问题是如果把这一些函数引入来?其实还有一些不通用的格式转换,这部分涉及到业务逻辑。比如:小王,我们想把name的插值部分改成王先生,或王小姐,这 就是和性别相关了。这种格式转换的代码只能用户自己写。那么又如何加入呢?

1.1构建Template对象

Ext.Template就是解决上面一些问题的。它的插值符号采用{},其格式采用

Java代码
  1. {name[:][format][(params)]}。

[]表达是可选的。可以分成两部分:第一部分是传入动态值的name,第二部分是调用格式化函数。

这个函数可以是Ext.util.Format中的函数,也可以是自己写的函数,对于自己写的函数,要注册到本Template的实例中来。函数的第一个参数是默认的后台传入的,是name的值。对于Ext.util.Format中的函数来讲,如ellipsis(10),只要函数名就可以,而不能用Ext.util.Format. ellipsis。第一参数是默认的后台传入的name的值,之后的参数传入就是该调用的参数的顺序,如ellipsis第二个参数就是10。如果只有一 个参数,函数的()可省,如{name:trim}。对于自己实现的模式化函数,后台只传两个参数,一是该name的值,二是改模板传入的所有的动态值 (values)。这也就是说我们在实现自己的格式化函数时,只能按这样的格式去写。接下来就怎么构建Template

构建一个Template的操作很简单。首先我们得定义模板内容。定义模板内容其实就是定义Html Segment。把动态的部分采用插值形式书写就可以。如:'

Java代码
  1. <spanclass="{cls}">{name:trim}{value:ellipsis(10)}</span>。

插值只要采用上面介绍的格式。


Ext.Template的构建函数采用更灵活的方式让我们去传入定义好的模板内容。我们可以把模板内容以数组的形式传进去,还可以以多个参数的形式传入(如

Java代码
  1. vart=newExt.Template('<divname="{id}">','<spanclass="{cls}">{name:trim}</span>','</div>');)。

Ext.Template的构建函数会自动按顺序串接这些字符串为模板内容。


传入了模板内容,可能我们还要进行一些配置 比如:不要编译,不要格式化,自己实现的格式化函数。Ext.Template的构建函数还提供一个config,我们可能通过这个参数来高设定我们需要的配置。如

Java代码
  1. {compiled:true,disableFormats:true,ToPrivateName:function(value,Values){}};。


1.2applyTemplate(Values)

构建Template对象之后,接下来要用它来生成模板生成内容。Ext.Template有 很多的实用方法,但最终都是调用applyTemplate(Values)来生成内容。而applyTemplate(Values)函数的作用也就是 把模板中插值用传入Values中的值来取代。模板内容是字符串形式,插值也是字符串形式,而value也可以看做字符串形式。这种对于string的操 作,当然就是要用string.replace()函数。

回忆一下replace(regexp,str)。我们会发现我们的需求和 与replace(regexp,str)有点不一样。我要的每当我找到一个插值(如{ name })时,我替换的内容是要根据插值(如{ name })中的内容(name)从传进来的values找到相同的名字的变量值。实际的会更复杂一点,假如插值中有格式化函数 ({value:ellipsis(10)}),那么我要取得改函数名,还有函数的参数值。也就是我们的需求是replace(regexp,str)的 第二个参数是function,而且能动态从第一个参数regexp的$1--$99传入该函数的参数值。

好像string. replace(regexp,str)没有办法实现,其实不然,它还有很少见的用法,就是为满足上面的需求。在baidu.google中也基本搜不 到。我们可以到mozilla 的文档去看看http://developer.mozilla.org/en/docs /Core_JavaScript_1.5_Reference:Global_Objects:String:repl,在mozilla 的文档中有这样定义:

Java代码
  1. varnewString=str.replace(regexp/substr,newSubStr/function[,flags]);

一种很少的的用法就是第二个参数可以是function ,而第一个参数中的regexp 中$1,$2做为函数的参数传到函数中,并执行该函数,返回结果,这结果当然是字符串,函数的第一个参数是该regexp 所匹配的字符串的内容,第二,三。。。的按$1,$2的顺序传参数。

接下来,我们就应该了解该regexp的用法,把插值 {name[:][format][(params)]}分成$1,$2,$3这样的值做为参数值传到replace(reg,function)的函数 参数中去。//{([/w-]+)(?:/:([/w/.]*)(?:/((.*?)?/))?)?/}/g就完成这个任务,把插值中的name存在$1 中,把format存在$2中,把params存在$3中。String. replace(reg,function)会把reg查找到的str做为第一个参数传到function的第一个参数中,$1,$2,$3第二,三,四 个参数中。

现在主要任务就是实现该函数。之后用string. replace(reg,function)就可以一条语句实现。该函数有三个任务:第一个任务就是按名字映射,动态把插值中的name映射到values[name]的值,第二任务是通用格式化,调用Ext.util.Format中的一些函数来格式化通过命名映射找到值,比如trim(values[name])。第三个任务就是调用一些和业务相关的格式转换,比如:把小李变成李先生。该函数如下:

Java代码
  1. varfn=function(m,name,format,args){
  2. if(format&&useF){//判断有没有format函数或是否允许使用format
  3. if(format.substr(0,5)=="this."){
  4. //调用自己实现并注册到该Template对象中方法用于业务相关的格式转换,
  5. returntpl.call(format.substr(5),values[name],values);
  6. }else{
  7. if(args){//去掉参数中的引号
  8. varre=/^/s*['"](.*)["']/s*$/;
  9. args=args.split(',');
  10. for(vari=0,len=args.length;i<len;i++){
  11. args[i]=args[i].replace(re,"$1");
  12. }
  13. args=[values[name]].concat(args);
  14. }else{
  15. args=[values[name]];
  16. }
  17. //执行Ext.util.Format中的格式转换函数。参数为默认+插值中传入值
  18. returnfm[format].apply(fm,args);
  19. }
  20. }else{
  21. //命名映射
  22. returnvalues[name]!==undefined?values[name]:"";
  23. }
  24. };
Java代码
  1. <p>在上面有一个地方要注意</p>
  2. <p></p>
  3. <prename="code"class="java">returntpl.call(format.substr(5),values[name],values);</pre>
  4. <p></p>
  5. <p>这个call和fucntion的.call,apply是不一样的。它是调用该类中自己的call函数:</p>
  6. <p></p>
  7. <prename="code"class="java">function(fnName,value,allValues){
  8. returnthis[fnName](value,allValues);},
  9. </pre>
  10. <p></p>
  11. <p>其作用就是调用this中同名的函数,把value,allValues作为参数传进去。<br><br>1.3compile()<br><br>接下来我们就是考虑Template的 效率问题。模版是重用的,但applyTemplate(Values)每次都要用regexp在模板内容中来插值和replace这些插值,这是一个相 当耗时的过程。我们细想一下,在Values设定之前,能不能把这些插值的位置给找到,等Values传进来时就直接把这些位置的置换就可以了。对于 string来讲,还是做标识(和插值一样),要么采用性能高效的查询算法,这样的话就用不上regexp。如果要用的话,就提高不了效率 了。<br><br>有没有更好的办法呢?可以不可以采用分块的思想,把模板内容的string分成若干小部分,比如采用插值 的边界来划。这样在数组找到插值的位置就快多了。当替换插值之后,就可以把整个数组给合并成string。而JavaScript数组的join()方式 有一个很好的特性,就是对于数组元素不是str,会先转换成string。如是expr,Js语句会先执行改语句生成string。我们可以充分利用 这个特性,把插值部分用Js语句要替换,等到Values传进来时就调用改数组的join()方法就可以。什么时候传Values值,怎么传呢?这得是 由用户说了算,这样最好的办法就是生成一个函数让用户在需要的时候去调用,并传入values。<br><br>基本思想已经 定了,接下来要做的就是如何去拼凑构建compiled(value)函数了,构建完了,用户就可以调用了。JavaScript的强大灵活之处很大部分 就是能动态构建函数。我们需要的函数的形式如下:</p>
  12. <p></p>
  13. <prename="code"class="java">this.compiled=function(values){return['<divstyle=/”…./”>',
  14. fm.trim(values['name']),'</div><divstyle=/”…./”>',fm.toDate(values['birthday']),'</div>'].join('');};</pre>
  15. <p><br><br>给定函数的字符串形式,现在就是如何去拼凑了,拼凑完之后通过eval()就变成函数。<br>下面的代码就是主要拼凑工作:</p>
  16. <p></p>
  17. <prename="code"class="java">varfn=function(m,name,format,args){
  18. if(format&&useF){
  19. args=args?','+args:"";
  20. if(format.substr(0,5)!="this."){
  21. format="fm."+format+'(';
  22. }else{
  23. format='this.call("'+format.substr(5)+'",';
  24. args=",values";
  25. }
  26. }else{
  27. args='';format="(values['"+name+"']==undefined?'':";
  28. }
  29. //',fm.xxxf(values['namex'],xx,yy),'
  30. //',this.call('xxxf',values['namex'],values),'
  31. //',(values['namex']==undefined?'':values['namex']),'
  32. return"'"+sep+format+"values['"+name+"']"+args+")"+sep+"'";
  33. };
  34. 我们可以看出它拼成三种字符串的形式,也就上一节提到的三种任务。接下来的:
  35. body=["this.compiled=function(values){return['"];
  36. body.push(this.html.replace(////g,'////').replace(/(/r/n|/n)/g,'//n').replace(/'/g,"//'")
  37. .replace(this.re,fn));
  38. body.push("'].join('');};");
  39. body=body.join('');
  40. </pre>
  41. <p></p>
  42. <p>就拼凑成了我们想要的函数字符串。body是字符串的形式,通过eval(body),就动态地生成了compiled(values)这样的函数。之后我们只要调用这个函数就可以了。<br><br>1.4小结</p>
  43. <p><br>Ext.Template还提供了一些其它的方法,用于把生成的模板内容插到DomElement中,这些很简单。Ext.template完成模板的插值的填充功能,这是模板的最基本的功能,模板还有一部分功能就是指令功能,比如,for指令,if指令等没有实现。</p>
  44. <p>
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics