1. 程式人生 > >【仿doT前端模板】二、if else

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

查看 繼續 ons cnblogs 第一個 關系 light 註意 參考

效果預覽

首先,按照慣例,我們先看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 = /\{\{\=(.*)\}\}/g;
 
var IF = /\{\{\?([^\?]{1}.*?)\}\}/g;
//var IF = /\{\{\?([^\?]+.*?)\}\}/g;
var ELSE_IF = /\{\{\?\?(.+?)\}\}/g;
var ELSE = /\{\{\?\?\}\}/g;
var END_IF = /\{\{\?\}\}/g;
 
 
jst.template = function(templateText) {
 
    // ↓這個就是實際運行的模板函數
    return function(it) {
        // 值綁定
        // var result = templateText;
        var result = templateText.replace(VALUE, "‘+($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