其实我一开始只是想了解{{#def.key}}和{{## #}}之间的关系以及如何使用,官网上对于{{## #}} 和 {{# }}定义如下:
{{# }} for compile-time evaluation/includes and partials
{{## #}} for compile-time defines
我英文不是很出类拔萃的那种,所以,我就翻译成 ‘在编译时需要诊断或包含的和其余部分’!好了,我完全不知道我在说什么了,之所以研究doT完全是就是因为理解不了这句话,为了给我的英语水平讨个说法。就开始看源码,doT.js虽然只有100来行代码,但是我看了60分钟,之后我便决定就不再自取其辱了!通过大胆猜测和合理怀疑去弄清每个函数作用,达到窥之一二, 其中借鉴了http://www.cnblogs.com/strayling/p/3779144.html,此文章对源码做了进一步的分解,感兴趣的可以看下。
Part I: 自我学习
doT.template函数接受3个参数,doT.template(tmpl,c,def),这3个参数是函数形参,源码里面是如此定义。
tmpl为模板字符串(就是被标签包裹的字符串),这些字符串最后会被处理成
html字符串。c为传入的配置项,如果不传,则为doT.templateSettings:
templateSettings: {
evaluate: /\{\{([\s\S]+?(\}?)+)\}\}/g,
interpolate: /\{\{=([\s\S]+?)\}\}/g,
encode: /\{\{!([\s\S]+?)\}\}/g,
use: /\{\{#([\s\S]+?)\}\}/g,
useParams: /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,
define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,
defineParams:/^\s*([\w$]+):([\s\S]+)/,
conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,
iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,
varname: "it",
strip: true,
append: true,
selfcontained: false,
doNotSkipEncoded: false
}
如果好奇配置项的功能,可以参照http://www.cnblogs.com/strayling/p/3779144.html,这篇文章已经做了分析。
3.def传入的是形如 {{#def.key}}的字符串,官方解释是for compile-time evaluation/includes and partials. 根据自己理解,我理解成 “一开始在script里面的模板字符串是{{=it.key1}},但是我又想多要写几个{{=it.key2}}{{=it.key3}},又不能继续网script标签里面添加,只好用这个#def.key把新增的模板字符的形式传入”
让我们具体分析下这3个参数的作用吧,一切的开始都是doT.template,那么我们就从这个函数出发,一个问题一个问题的搞清楚。
例1:
<div id="content"></div>
<script id="pagetmpl" type="text/x-dot-template">
<h2>Here is the page using a header template</h2>
{{=it.name}}
</script>
<script type="text/javascript">
var data = {
name: "My name"
};
var pagefn = doT.template(document.getElementById('pagetmpl').text)
document.getElementById('content').innerHTML = pagefn(data);
</script>
var pagefn = doT.template(document.getElementById('pagetmpl').text)
只接受了tmpl这个参数。- 函数内部,先通过resolveDefs,将tmpl处理一下,这个步骤是通过正则检查{{## #}} 和 {{# }},如果没有,就原封不动返回,
var str = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl
; 这里没有{{## #}} 和 {{# }},所以返回'<h2>Here is the page using a header template</h2>{{=it.name}}'
- 然后模板字符串str的值已经处理完毕,回到doT.template主函数体内,又经过各种replace校验和编译,通俗了说就是处理在模板字符串里面的 {{= }},{{ }} ,{{~ }} ,{{? }} ,{{! }},此处省略不做详细介绍,编译完成后返回一个函数,源码里写道:
return new Function(c.varname, str);
new Function是创建一个函数,c.varname是函数的参数,由于c没有传,所以c为默认配置c.varname就是templateSettings.varname,名称为it。str是要执行的函数体,并且str又是经过一系列编译后的字符串,在此例子中为:"var out='<h2>Here is the page using a header template</h2>'+(it.name);return out;"
返回的函数赋值给pagefn:
var pagefn = function (it) {
var out='<h2>Here is the page using a header template</h2>'+(it.name);
return out;
}
- 然后为pagefn传入data, 得到结果
out='<h2>Here is the page using a header template</h2>'+"My name"
,
之后document.getElementById('content').innerHTML = pagefn(data)
渲染dom
我说过,我研究doT.js的目的是{{## #}} 和 {{# }},所以需要逐步解开这个谜团,继续走下去。
例2:
<script id="pagetmpl" type="text/x-dot-template">
<h2>Here is the page using a header template</h2>
{{#def.header}}
{{=it.name}}
</script>
<div id="content"></div>
<script type="text/javascript">
var def = {
header: '<h1>{{=it.title}}</h1>'
};
var data = {
title: "My title",
name: "My name"
};
var pagefn = doT.template(document.getElementById('pagetmpl').text, undefined, def);
document.getElementById('content').innerHTML = pagefn(data);
</script>
在例1的基础上加了点东西,模板字符串里面多了{{#def.header}},为doT.template准备的第三个参数def = {header: '<h1>{{=it.title}}</h1>'}
var pagefn = doT.template(document.getElementById('pagetmpl').text, undefined, def)
,和例1不一样,这个时候函数接受了第二个参数和第三个参数,这里第二个参数c为undefined,说明我们放弃自定义配置,用默认配置,我们也可以用包装好的函数compile,var pagefn = doT.compile(tmpl,def)
,效果一样,此时的tmpl变成"<h2>Here is the page using a header template</h2>{{#def.header}}{{=it.name}}"
,- 在函数内部,调用resolveDefs,var str = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl;经过resolveDefs(c, block, def),传入c(配置),block(字符串),def(待编译部分),通过def将block中{{#def.header}}处理成{{=it.key}},
{{#def.header}}由replace函数将传入doT.template的def按照键值进行替换。def.header是一开始就定义好被传进来的,变化过程如下:
局部:{{#def.header}} => <h1>{{=it.title}}</h1>
全局:
"<h2>Here is the page using a header template</h2>{{#def.header}}{{=it.name}}"
=>
"<h2>Here is the page using a header template</h2><h1>{{=it.title}}</h1>{{=it.name}}"
- 然后就是按部就班,最后编译的结果
var pagefn = function(it) {
var out='<h2>Here is the page using a header template</h2><h1>'+(it.title)+'</h1>'+(it.name);
return out;
}
之后便是给pagefn传入data得到函数体中的变量,最后进行innerHTML操作,document.getElementById('content').innerHTML = pagefn(data)
;此外{{#def.key}} 还可以这么玩, var def = {key: “{{#def.childKey1}}{{#def.childKey2}}” },resolveDefs会通过递归找到最里面那层,不过传入的data就必须有childKey1和childKey2的定义
例3:
<script id="pagetmpl" type="text/x-dot-template">
<h2>Here is the page using a header template</h2>
{{## def.customeKey:
<div>{{=it.name}} is not {{=it.fakeAge}}</div>
#}}
{{#def.header}}
{{#def.customeKey}}
{{=it.name}}
</script>
<div id="content"></div>
<script type="text/javascript">
var def = {
header: '<h1>{{=it.title}}</h1>'
};
var data = {
title: "My title",
name: "My name",
fakeAge: 11
};
var pagefn = doT.template(document.getElementById('pagetmpl').text, undefined, def);
document.getElementById('content').innerHTML = pagefn(data);
</script>
官网上对于{{## #}} 和 {{# }}定义如下
{{# }} for compile-time evaluation/includes and partials
{{## #}} for compile-time defines
根据定义{{## #}}是为了编译过程中的defines,结合代码,这句可以翻译成:
“我在模板字符串里面有一个{{#def.customeKey}},但是糟了,我传进去的def没有customeKey这个键值对怎么班,那就直接在模板字符串里面补充定义下”,
现在分析代码:
首先def,tmpl传入dot.template函数,然后通过resolveDefs(c, block, def),根据def参数对模板字符串block通过replace函数进行处理,首先对{{## #}}部分进行处理,获取’customeKey’,使得def['customeKey'] = '<div>{{=it.name}} is not {{=it.fakeAge}}</div>'
, 然后将{{## #}}这一部分替换为空字符串”。
模板字符串发生变化:
<h2>Here is the page using a header template</h2>
{{## def.customeKey:
<div>{{=it.name}} is not {{=it.fakeAge}}</div>
#}}
{{#def.header}}
{{#def.customeKey}}
{{=it.name}}
=>
<h2>Here is the page using a header template</h2>
{{#def.header}}
{{#def.customeKey}}
{{=it.name}}
补充编译的def参数变化(发生在resolveDefs函数的作用域内):
var def = {
header: '<h1>{{=it.title}}</h1>'
};
=>
var def = {
header: '<h1>{{=it.title}}</h1>',
customeKey: '<div>{{=it.name}} is not {{=it.fakeAge}}</div>'
};
然后进行对{{#def.key}}的解析:
<h2>Here is the page using a header template</h2>
{{#def.header}}
{{#def.customeKey}}
{{=it.name}}
=>
<h2>Here is the page using a header template</h2>
<h1>{{=it.title}}</h1>
<div>{{=it.name}} is not {{=it.fakeAge}}</div>}
{{=it.name}
之后的过程和第二个例子毫无差别经过一系列对{{= }} , {{ }} ,{{~ }} ,{{? }} ,{{! }}的处理后,doT.tempalte返回一个函数
pagefn = function(it){
var out='<h2>Here is the page using a header template</h2><h1>'+(it.title)+'</h1><div>'+(it.name)+' is not '+(it.fakeAge)+'</div>'+(it.name);
return out;
}
然后为pagefn传入data得到函数体中的变量,最后进行innerHTML操作,document.getElementById('content').innerHTML = pagefn(data
);
Part II 思考:
我想def的存在是为了模块化,否则,对于多种不同组合的模板字符串来说,有多少中组合,就得重复写n个,远不如模块化方便简洁。
简单例子:
<script id="pagetmpl" type="text/x-dot-template">
{{#def.customKey}}
</script>
<div id="content"></div>
<script type="text/javascript">
var def1 = {
customKey: '<h1>{{=it.title}}</h1><h1>{{=it.name}}</h1><h1>{{=it.fakeAge}}</h1>'
};
var def2 = {
customKey: '<h1>{{=it.title}}</h1><h1>{{=it.name}}</h1><h1>{{=it.fakeAge}}</h1><h1>{{=it.newTitle}}</h1><h1>{{=it.newName}}</h1><h1>{{=it.newfakeAge}}</h1>'
};
var data = {
title: "My title",
name: "My name",
fakeAge: 11,
newTitle: 'My newTitle',
newName: 'My newName',
newfakeAge: 22
};
var pagefn = doT.compile(document.getElementById('pagetmpl').text, def1);
var pagefn = doT.compile(document.getElementById('pagetmpl').text, def2);
document.getElementById('content').innerHTML = pagefn(data);
通过对传入的def控制,很容易改变模板内容。
总结:
以上是我的学习成果,如果有写的不好或者错误的,肯定是我的能力有限,请指正。
通过分析源代码,理解清楚这个引擎工作的流程,对于熟练使用此工具有益无害,现在掉下头看其基本用法,理解起来不像一开始那样死记硬背,不过下面的
也是我从百度粘贴过来的,由于不管是谁的博客都是一样的,所以我也不知道下面的代码到底是谁是原作者,我也就不加连接了。
1、for interpolation 赋值
格式:{{= }}
数据源:{"name":"Jake","age":31}
区域:<div id="interpolation"></div>
模板:
<script id="interpolationtmpl" type="text/x-dot-template">
<div>Hi {{=it.name}}!</div>
<div>{{=it.age || ''}}</div>
</script>
调用方式:
var dataInter = {"name":"Jake","age":31};
var interText = doT.template($("#interpolationtmpl").text());
$("#interpolation").html(interText(dataInter));
2、for evaluation for in 循环
格式:
{{ for var key in data { }}
{{= key }}
{{ } }}
数据源:
{"name":"Jake",
"age":31,
"interests":["basketball","hockey","photography"],
"contact":{"email":"jake@xyz.com","phone":"999999999"}
}
区域:<div id="evaluation"></div>
模板:
<script id="evaluationtmpl" type="text/x-dot-template">
{{ for(var prop in it) { }}
<div>KEY:{{= prop }}---VALUE:{{= it[prop] }}</div>
{{ } }}
</script>
调用方式:
var dataEval = {"name":"Jake","age":31,"interests":["basketball","hockey","photography"],"contact":{"email":"jake@xyz.com","phone":"999999999"}};
var evalText = doT.template($("#evaluationtmpl").text());
$("#evaluation").html(evalText(dataEval));
3、for array iteration 数组
格式:
{{~data.array :value:index }}
...
{{~}}
数据源:{"array":["banana","apple","orange"]}
区域:<div id="arrays"></div>
模板:
<script id="arraystmpl" type="text/x-dot-template">
{{~it.array:value:index}}
<div>{{= index+1 }}{{= value }}!</div>
{{~}}
</script>
调用方式:
var dataArr = {"array":["banana","apple","orange"]};
var arrText = doT.template($("#arraystmpl").text());
$("#arrays").html(arrText(dataArr));
4、{{? }} for conditionals 条件
格式:
{{? }} if
{{?? }} else if
{{??}} else
数据源:{"name":"Jake","age":31}
区域:<div id="condition"></div>
模板:
<script id="conditionstmpl" type="text/x-dot-template">
{{? !it.name }}
<div>Oh, I love your name, {{=it.name}}!</div>
{{?? !it.age === 0}}
<div>Guess nobody named you yet!</div>
{{??}}
You are {{=it.age}} and still dont have a name?
{{?}}
</script>
调用方式:
var dataEncode = {"uri":"http://bebedo.com/?keywords=Yoga","html":"<div style='background: #f00; height: 30px; line-height: 30px;'>html元素</div>"};
var EncodeText = doT.template($("#encodetmpl").text());
$("#encode").html(EncodeText(dataEncode));
5、for interpolation with encoding
数据源:{"uri":"http://bebedo.com/?keywords=Yoga"}
格式:{{!it.uri}}
区域:<div id="encode"></div>
模板:
<script id="encodetmpl" type="text/x-dot-template">
Visit {{!it.uri}} {{!it.html}}
</script>
调用方式:
var dataEncode = {"uri":"http://bebedo.com/?keywords=Yoga","html":"<div style='background: #f00; height: 30px; line-height: 30px;'>html元素</div>"};
var EncodeText = doT.template($("#encodetmpl").text());
$("#encode").html(EncodeText(dataEncode));
Part III 遗留问题:
doT.js在node.js中的应用,由于时间太晚了,我不想看了,下次再整理并试验下
参考连接:
http://olado.github.io/doT/index.html
https://github.com/olado/doT/blob/master/examples/browsersample.html
https://github.com/olado/doT/blob/master/examples/advancedsnippet.txt
http://www.cnblogs.com/strayling/p/3779144.html