`
jljlpch
  • 浏览: 319368 次
  • 性别: Icon_minigender_1
  • 来自: 南昌
社区版块
存档分类
最新评论

Ext.template分析

    博客分类:
  • Ext
阅读更多

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

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

 

<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就是解决上面一些问题的。它的插值符号采用{},其格式采用

 

{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。把动态的部分采用插值形式书写就可以。如:'

 

<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>。

 

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


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

 

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

 

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


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

 

{ 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 的文档中有这样定义:

 

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])。第三个任务就是调用一些和业务相关的格式转换,比如:把小李变成李先生。该函数如下:

 

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] : ""; 
} 
}; 

 在上面有一个地方要注意

 

return tpl.call(format.substr(5), values[name], values); 

 

这个call和fucntion的.call,apply是不一样的。它是调用该类中自己的 call 函数:

 

 function(fnName, value, allValues){ 
return this[fnName](value, allValues); }, 

 

 其作用就是调用this中同名的函数,把value, allValues作为参数传进去。

1.3compile()

接下来我们就是考虑Template的效率问题。模版是重用的,但applyTemplate(Values)每次都要用regexp在模板内容中来插值和replace这些插值,这是一个相当耗时的过程。我们细想一下,在Values设定之前,能不能把这些插值的位置给找到,等Values传进来时就直接把这些位置的置换就可以了。对于string来讲,还是做标识(和插值一样),要么采用性能高效的查询算法,这样的话就用不上regexp。如果要用的话,就提高不了效率了。

有没有更好的办法呢?可以不可以采用分块的思想,把模板内容的string分成若干小部分,比如采用插值的边界来划。这样在数组找到插值的位置就快多了。当替换插值之后,就可以把整个数组给合并成string。而JavaScript数组的join()方式有一个很好的特性,就是对于数组元素不是str,会先转换成string。如是expr, Js 语句会先执行改语句生成string。我们可以充分利用这个特性,把插值部分用Js 语句要替换,等到Values传进来时就调用改数组的join()方法就可以。什么时候传Values值,怎么传呢?这得是由用户说了算,这样最好的办法就是生成一个函数让用户在需要的时候去调用,并传入values。

基本思想已经定了,接下来要做的就是如何去拼凑构建compiled(value)函数了,构建完了,用户就可以调用了。JavaScript的强大灵活之处很大部分就是能动态构建函数。我们需要的函数的形式如下:

 

this.compiled = function(values){ return ['<div style=\”….\”>', 
fm.trim(values['name']),'</div><div style=\”….\”>', fm.toDate(values['birthday']),'</div>'].join('');}; 

 

给定函数的字符串形式,现在就是如何去拼凑了,拼凑完之后通过eval()就变成函数。
下面的代码就是主要拼凑工作:

 

var fn = function(m, name, format, args){ 
if(format && useF){ 
args = args ? ',' + args : ""; 
if(format.substr(0, 5) != "this."){ 
format = "fm." + format + '('; 
}else{ 
format = 'this.call("'+ format.substr(5) + '", '; 
args = ", values"; 
} 
}else{ 
args= ''; format = "(values['" + name + "'] == undefined ? '' : "; 
} 
//',fm.xxxf(values['namex'],xx,yy),' 
//',this.call('xxxf',values['namex'],values),' 
//',(values['namex']==undefined?'':values['namex']),' 
return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'"; 
}; 

我们可以看出它拼成三种字符串的形式,也就上一节提到的三种任务。接下来的: 

body = ["this.compiled = function(values){ return ['"]; 
body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'") 
.replace(this.re, fn)); 
body.push("'].join('');};"); 
body = body.join(''); 

 

 就拼凑成了我们想要的函数字符串。body是字符串的形式,通过eval(body),就动态地生成了compiled(values)这样的函数。之后我们只要调用这个函数就可以了。

1.4小结


Ext.Template还提供了一些其它的方法,用于把生成的模板内容插到Dom Element中,这些很简单。Ext.template完成模板的插值的填充功能,这是模板的最基本的功能,模板还有一部分功能就是指令功能,比如,for指令,if指令等没有实现。

                                                                                                              prk   2008-7-28

分享到:
评论
1 楼 xxf_cz 2008-08-23  
for指令,if指令等 在 Ext.XTemplate 中实现了

相关推荐

Global site tag (gtag.js) - Google Analytics