首页 > 代码库 > 【仿doT前端模板】二、if else

【仿doT前端模板】二、if else

效果预览

首先,按照惯例,我们先看doT 实现的效果:

模板:

{{? it.name }}
    <div>嗨, {{=it.name}}!</div>
{{?? it.age === 0}}
    <div>我猜应该还没人给你起名字吧?</div>
{{??}}
    你已经 {{=it.age}} 岁了但是你还没有名字?
{{?}}

 

数据结果
{name:‘十一川‘} <div>嗨, 十一川! </div>
{age:0} <div>我猜应该还没人给你起名字吧?</div>
{age:11} <div>你已经 11 岁了但是你还没有名字?</div>

实现思路

我们此次要做的事情,与上次一样,无非是一个 查找标记 => 替换成对应代码 的过程 而区别在于,我们此次要替换的标记不再是一个,而是4个:

标记 结果
{{? it.name }} => if( it.name ){
{{?? it.age === 0}} => } else if( it.age === 0 ) {
{{??}} => } else{
{{?}} => }

实现过程

写出正则表达式

var IF = /\{\{\?([^\?]{1}.*?)\}\}/g;
var ELSE_IF = /\{\{\?\?(.+?)\}\}/g;
var ELSE = /\{\{\?\?\}\}/g;
var END_IF = /\{\{\?\}\}/g;

 

这里需要特别注意的是,匹配if 与其他标记的包含关系,也就是说,如果我们匹配的是:

技术分享

 

那么,else标记的{{??}}中的第二个? 会被当成条件,也就是说:

技术分享

 

甚至于,我们作为结尾的{{?}}也会被当成没有条件的if语句:

技术分享

 

技术分享

 

这很明显不是我们想要达到的效果啊!

要解决这个问题,首先能想到的简单粗暴的办法就是。。。我们可以先把{{??}}{{?}}标记替换掉嘛,这样不就等到替换{{? 条件}}的时候,我们就能保证全部都是条件语句了。

唔。。。这固然是个“暂时能用的办法”,但是万一哪天,我们心血来潮地把赋值{{= 值}}改成了这样:{{??? 值}},那我们还得去代码里看看值的替换是否在if替换之前?

技术分享

 

有!我们不难推导出,这里的“条件”要先符合两个条件:

  • 第一个字符不能是?这个字符本身
  • 不能为空

这样的话,不管我们替换的顺序如何,都能保证不会影响到其他的标记,那么,这两个限定又如何通过正则表达式表达出来呢?

我们可以通过^符号来表示“不能是某个字符”,而我们要表示“不能是?的一个字符”的话自然就是[^\?];而{1}来表示“有且只有一个字符”。因此([^\?]{1}.*?)表示的就是“最起码一个字符,而且第一个字符不能是?的任意长度字符串” ,自然就是我们一开始提到的if的正则表达式。

顺带一提,为什么只限制了“第一个字符不能是?这个字符”,而不是“所有字符”呢?这是因为三元操作符?:能够产生一个合法的bool值,比如:a?2:3,如果我们只是粗暴地规定“所有的条件中都不能包含?”那么可能会导致原本合法的表达式没有被当成条件。而这个表达式的?前面最起码要有一个变量名,变量名最短也是一个非?的字符,因此在这个层面,我们仅仅限定住“第一个字符”,是有着必要性和充分性的。

而类似的,为了防止 else 的标记被当成“没有条件的 else if 标记” 我们同样要对这里的条件作出“起码有一个字符的限制” ,也就是(.+?)

引入out

现在,我们还有一个问题是,因为引入了判断机制,我们编译之后的代码不能再像之前那样像一条串似的直接拼接起来。我们在第一篇中编译后的代码大概类似于这样:return ‘H1!‘+it.name;可是,我们只进行了判断,没有进行字符串拼接的话,代码大概是这样:

return if(it.name){
    ‘Hi!‘+it.name;
}

技术分享

 

连语法都不正确了啊喂!不要慌,总之先冷静下来找时光机。。。啊不对,是先回想正常情况下我们是如何完成类似的拼接的:

var out="";
 
if(it.name){
    out += ‘Hi!‘+it.name;
}
 
return out;

因此类似的,我们要引入一个变量out,这样我们就能在判断体内把结果拼接起来了。于是,我们要替换的目标变成了这样:

标记 结果
{{? it.name }} => ‘; if( it.name ){ out += ‘
{{?? it.age === 0}} => ‘; } else if( it.age === 0 ) { out += ‘
{{??}} => ‘; } else{ out += ‘
{{?}} => ‘; } out += ‘

我们能这么做的前提,是我们假设,在进行判断语句之前,这个字符串还没有结束。 于是,我们加上单引号使其提前结束(我们使用单引号来标记字符串,还记得吗?)在执行完判断之后,我们又通过out+=‘来继续拼接字符串。也就是说,在执行判断之前,我们的字符串是未结束的状态,在执行完判断之后,我们的字符串依然是未结束的状态

实现过程

好了,剩下的就只有将刚刚的思考转化为实际代码的过程了,我建议你自己手动完成这部分,如果你实在不知如何下手的话,这里有一份代码供你参考:

点击这里直接运行试试

HTML:

技术分享
<div id="target"></div>
 
<script type="text/x-dot-template" class="js-template">
{{? it.name }}
<div>嗨, {{= it.name}}!</div>
{{?? it.age === 0}}
<div>我猜应该还没人给你起名字吧?</div>
{{??}}
<div>你已经 {{= it.age}} 岁了但是你还没有名字?</div>
{{?}}
</script>
View Code

 

JavaScript(需要先引入jQuery):

技术分享
var jst = {};
 
var VALUE = http://www.mamicode.com/{/{/=(.*)/}/}/g;"‘+($1)+‘");
        var ev = result;
 
        // if && else
        // 这里是为了把字符 if else 里面的内容提取出来
        ev = ev.replace(IF, "‘; if(($1)){out +=‘ ")
            .replace(ELSE, "‘}else{out += ‘ ")
            .replace(ELSE_IF, "‘;}else if(($1)){ out += ‘ ")
            .replace(END_IF, "‘}; out+=‘ ");
 
        ev = "var out = ‘‘; out+=‘  " + ev.split(‘\n‘).join("") + "‘ ;";
 
        //console.log(ev);
        // debugger
        return eval(ev);
    }
}
 
    //运行部分:
    var func = jst.template($(‘.js-template‘).html());
 
    $(‘#target‘).html(func({
        name:‘十一川‘,
        age:0
    }));
View Code

 

【仿doT前端模板】二、if else