4.2.1:尝试匹配(?:[^\}]|\}(?!\}))*?,这里实际就是两个部分[^\}]|\}和(?!\}),这里的正则写的有点复杂,其实也不难理解。这两个匹配他使用(?:)*?表示匹配后不捕捉,并且是惰性匹配,而却在它的外层加了()?,表示捕获分组,可想而 知是为了能更多的捕捉到全部的全部条件的字符串,因为里层的是惰性匹配,所以系统默认不匹配,继续后面的匹配
5:尝试匹配(?:\s+(.*?)?)?,发现i无法与\s+匹配,匹配失败,返回到惰性匹配那。
6:尝试让惰性匹配(?:[^\}]|\}(?!\}))*?去匹配字符串,我们先看一下[^\}]|\}(?!\}),这样看,以|为分割点,左边是[^\}],右边是\}(?!\}),这就清楚了,可以匹配非}的字符,如果匹配失败,就匹配},但是它的后面不能再有},所以系统先使用[^\}]去匹配i,再去执行5,如果5仍不能满足,则继续匹配i,直到5匹配满足,而此时系统已经匹配到了(i,lang)
7:(?:\s+(.*?)?)?中的(.*?)?依旧是惰性匹配,系统先尝试不匹配
8:尝试匹配(?:\(((?:[^\}]|\}(?!\}))*?)?\))?,发现匹配失败,因为量词的缘故,继续后续的匹配
9:尝试匹配\s*\}\},如果从$开始匹配,果断匹配失败。
10:返回到惰性匹配那,让(.*?)尝试匹配L,再执行8,9步,直到它能满足,如果不能正则匹配不成功。最后(.*?)匹配了Langs,完成了整个正则的匹配。
那{{/each}}则就是一个道理。但要注意这个/,因为如果/匹配了,那replace匹配函数中的slash将会是/,则根据tag[ slash ? "close" : "open" ],它将使用tag['close']来闭合这个each,这也就是为什么拥有open的close的原因。
关于each是如何实现的,我们需要看到源码的这个部分:
"each": { _default: { $2: "$index, $value" }, open: "if($notnull_1){$.each($1a,function($2){with(this){", close: "}});}" }
replace的匹配方法中有7个参数,其中type参数就是each,根据
var tag = jQuery.tmpl.tag[ type ]
这里我们可以看到其实实现each的功能仅仅是将$.each写入字符串中,它的参数有$index和$value,这其实就是jQuery的each方法。代码的后续会将其取出,进行拼接。
4.if和else的用法
<script type="text/html" id="template1"> <tr> <td>${ID}</td> <td>${Name}</td> <td> {{if Langs.length > 1}} ${Langs.join('; ')} {{else}} ${Langs} {{/if}} </td> </tr> </script> var users = [ { ID: 'think8848', Name: 'Joseph Chan', Langs: [ 'Chinese', 'English' ] }, { ID: 'aCloud', Name: 'Mary Cheung', Langs: [ 'Chinese', 'French' ] } ] $('#template1').tmpl(users).appendTo('#table1');
其实if,else跟each差不多在正则匹配的时候,这里我就不重复了。看一下对应的函数
"if": { open: "if(($notnull_1) && $1a){", close: "}" }, "else": { _default: { $1: "true" }, open: "}else if(($notnull_1) && $1a){" },
注意一下,在这里if拥有close而else则没有,反映到模版书写上,闭合的时候我们只需要写{{/if}}就可以了,不需要写{{/else}}
5.html占位符
<script type="text/html" id="template1"> <tr> <td>${ID}</td> <td>${Name}</td> <td>{{html Ctrl}}</td> </tr> </script> var users = [ { ID: 'think8848', Name: 'Joseph Chan', Ctrl: '<input type="button" value="Demo"/>' }, { ID: 'aCloud', Name: 'Mary Cheung', Ctrl: '<input type="button" value="Demo"/>' } ]; $('#template1').tmpl(users).appendTo('#table1') $('table').delegate('tr','click',function(){ var item = $.tmplItem(this); alert(item.data.Name); })
<table id="table1"></table>
这里看一下模版的{{html Ctrl}},匹配规则还是一样的。看一下拓展的部分:
"html": { // Unecoded expression evaluation. open: "if($notnull_1){__.push($1a);}" }
注意,这时允许你脚本插入的,也就是如果你插入一个<script type="text/javascript" >alert(1)<\/script>,生成的页面是可以弹出alert(1)的。这跟跟换ID和Name是一个意思。
6.{{tmpl}}
<script type="text/html" id="template1"> <tr> <td>${ID}</td> <td>${Name}</td> <td>{{tmpl($data) '#template2'}}</td> </tr> </script> <script type="text/html" id="template2"> {{each Langs}} ${$value} {{/each}} </script> var users = [ { ID: 'think8848', Name: 'Joseph Chan', Langs:[ 'Chinese', 'English' ] }, { ID: 'aCloud', Name: 'Mary Cheung', Langs: [ 'Chinese', 'French' ] } ]; $('#template1').tmpl(users).appendTo('#table1');
看一下{{tmpl($data) '#template2'}},正则匹配是跟以前一样的。我们看一下扩展
"tmpl": { _default: { $2: "null" }, open: "if($notnull_1){__=__.concat($item.nest($1,$2));}" }
注意里面有个方法nest,找到newTmplItem方法里的我们定义的newItem,看一下,它里面是否有个属性是nest,有,是tiNest,看一下tiNest
function tiNest( tmpl, data, options ) { jQuery.tmpl( jQuery.template( tmpl ), data, options, this ); }