Ext.template分析

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

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

 

Java代码
  1. <div style=”….”>小王</div><div style=”….”>1983-09-24</div>  
<div style=”….”>小王</div><div style=”….”>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[:][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. <span class="{cls}">{name:trim} {value:ellipsis(10)}</span>。  
<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>。

 

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


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

 

Java代码
  1. var t = new Ext.Template'<div name="{id}">','<span class="{cls}">{name:trim} </span>''</div>');)。  
var t = new Ext.Template( '<div name="{id}">','<span class="{cls}">{name:trim} </span>', '</div>');)。

 

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


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

 

Java代码
  1. { compiled:true,disableFormats:true ,ToPrivateName:function(value,Values){}};。   
{ 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. var newString = str.replace(regexp/substr, newSubStr/function[, flags]);   
var newString = 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. var fn = function(m, name, format, args){   
  2. if(format && useF){//判断有没有format函数或是否允许使用format   
  3. if(format.substr(05) == "this."){   
  4. //调用自己实现并注册到该Template对象中方法用于业务相关的格式转换,   
  5. return tpl.call(format.substr(5), values[name], values);   
  6. }else{   
  7. if(args){ //去掉参数中的引号   
  8. var re = /^/s*['"](.*)["']/s*$/;   
  9. args = args.split(',');   
  10. for(var i = 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. return fm[format].apply(fm, args);   
  19. }   
  20. }else{   
  21. //命名映射   
  22. return values[name] !== undefined ? values[name] : "";   
  23. }   
  24. };   
var fn = function(m, name, format, args){ 
if(format && useF){//判断有没有format函数或是否允许使用format 
if(format.substr(0, 5) == "this."){ 
//调用自己实现并注册到该Template对象中方法用于业务相关的格式转换, 
return tpl.call(format.substr(5), values[name], values); 
}else{ 
if(args){ //去掉参数中的引号 
var re = /^/s*['"](.*)["']/s*$/; 
args = args.split(','); 
for(var i = 0, len = args.length; i < len; i++){ 
args[i] = args[i].replace(re, "$1"); 
} 
args = [values[name]].concat(args); 
}else{ 
args = [values[name]]; 
} 
//执行Ext.util.Format中的格式转换函数。参数为默认+插值中传入值 
return fm[format].apply(fm, args); 
} 
}else{ 
//命名映射 
return values[name] !== undefined ? values[name] : ""; 
} 
}; 
Java代码
  1. <p> 在上面有一个地方要注意</p>  
  2. <p> </p>  
  3. <pre name="code" class="java">return tpl.call(format.substr(5), values[name], values); </pre>  
  4. <p> </p>  
  5. <p>这个call和fucntion的.call,apply是不一样的。它是调用该类中自己的 call 函数:</p>  
  6. <p> </p>  
  7. <pre name="code" class="java"> function(fnName, value, allValues){   
  8. return this[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. <pre name="code" class="java">this.compiled = function(values){ return ['<div style=/”…./”>',   
  14. fm.trim(values['name']),'</div><div style=/”…./”>', fm.toDate(values['birthday']),'</div>'].join('');}; </pre>  
  15. <p> <br><br>给定函数的字符串形式,现在就是如何去拼凑了,拼凑完之后通过eval()就变成函数。 <br>下面的代码就是主要拼凑工作:</p>  
  16. <p> </p>  
  17. <pre name="code" class="java">var fn = function(m, name, format, args){   
  18. if(format && useF){   
  19. args = args ? ',' + args : "";   
  20. if(format.substr(05) != "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. 我们可以看出它拼成三种字符串的形式,也就上一节提到的三种任务。接下来的:   
  36.   
  37. body = ["this.compiled = function(values){ return ['"];   
  38. body.push(this.html.replace(g, '').replace(/(/r/n|/n)/g, '//n').replace(/'/g, "//'")   
  39. .replace(this.re, fn));   
  40. body.push("'].join('');};");   
  41. body = body.join('');   
  42.   
  43. </pre>  
  44. <p> </p>  
  45. <p> 就拼凑成了我们想要的函数字符串。body是字符串的形式,通过eval(body),就动态地生成了compiled(values)这样的函数。之后我们只要调用这个函数就可以了。 <br><br>1.4小结 </p>  
  46. <p><br>Ext.Template还提供了一些其它的方法,用于把生成的模板内容插到Dom Element中,这些很简单。Ext.template完成模板的插值的填充功能,这是模板的最基本的功能,模板还有一部分功能就是指令功能,比如,for指令,if指令等没有实现。</p>  
  47. <p>   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值