理解doT.js {{## #}}

其实我一开始只是想了解{{#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个参数是函数形参,源码里面是如此定义。

  1. tmpl为模板字符串(就是被标签包裹的字符串),这些字符串最后会被处理成
    html字符串。

  2. 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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值