模板引擎的理解

  • 什么是模板引擎
    • 概念 :模板引擎是為了使用戶界面與業(yè)務(wù)數(shù)據(jù)(內(nèi)容)分離而產(chǎn)生的,它可以生成特定格式的文檔,用于網(wǎng)站的模板引擎就會(huì)生成一個(gè)標(biāo)準(zhǔn)的文檔
      就是將模板文件和數(shù)據(jù)通過模板引擎生成一個(gè)HTML代碼
      image.png
  • 如一個(gè)簡(jiǎn)單的需求
    1.數(shù)據(jù)來自一個(gè)數(shù)組
    2.不能寫死在頁面里


    image.png
songs === [
   {name: '紳士', url: 'http://music.163.com/xxx', singer: '薛之謙'},
   {name: '剛剛好', url: 'http://music.163.com/yyy', singer: '薛之謙'},
   ...
]

兩種方法:1.遍歷拼接HTML字符串 2.遍歷構(gòu)造DOM對(duì)象
一個(gè)簡(jiǎn)化的模板引擎

字符串格式化
before:
var li = '<li>' + song[i].name + '-' + song[i].singer + '</li>'

after:
var li = stringFormat('<li>{0} - {1}</li>', songs[i].name, songs[i].singer)

function stringFormat(string){ // string是變量不是屬性,不能使用this
    //params此時(shí)為string從1開始之后的數(shù)組
    var params = [].slice.call(arguments,1) // arguments為偽數(shù)組,數(shù)組的方法不能使用 call將arguments設(shè)為this

    var regex = /\{(\d+)\}/g  //匹配{一個(gè)或多個(gè)數(shù)字}
    //將字符串中的{n}替換為params[n]
    string = string.replace(regex,function(){
        //function的arguments[1]為字符串中的{n},arguments[0]為字符串中的{n}
        var index = arguments[1]
        console.log(arguments)
        return params[index]
    })//取代為function的返回值
    return string
}


  • 第一版模板引擎
模板文件 : var template = '<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>';

數(shù)據(jù): var data = {
    name: "Krasimir",
    age: 29
}
/*
regex.exec 正則,遍歷,匹配
exec在遍歷匹配的所有字符串,遍歷完后返回null,然后重新遍歷
[^%>]不是%或>任一個(gè)字符
()正則里分組的意思,分組就是我要用的意思,用括號(hào)里的東西
*/
模板引擎: var formatEngine = function (tpl,data){
    var regex = /<%([^%>]+)?%>/g
    while(match = regex.exec(tpl)){ //這里=是賦值
        tpl = tpl.replace(match[0],data[match[1]])
        console.log(tpl)
    }
    return tpl
}
HTML文件:
var string = formatEngine(template,data)
console.log(string)
  • 復(fù)雜邏輯版模板引擎
    預(yù)備知識(shí):
    new Function:根據(jù)字符串創(chuàng)建一個(gè)函數(shù)
var fn = new Function("num","console.log(num + 1)"
fn(1)//2
var fn = new Function("num","console.log(num + '1')"
fn(1)//11

等價(jià)于

var fn = function(num){
    console.log(num + 1);//函數(shù)體
}
fn(1) //2
  • 通過這種方法,我們可以根據(jù)字符串構(gòu)造函數(shù),包括他的參數(shù)和函數(shù)體,模板引擎最終返回一個(gè)編譯好的模板,例如
return 
"<p>Hello, my name is " + 
this.name + 
". I\'m " + 
this.profile.age + 
" years old.</p>";

把所有字符串放在一個(gè)數(shù)組里,在程序的最后把他們拼接起來

var arr = []
arr.push("<p>Hello, my name is " )
arr.push(this.name)
Arr.push("i am");
Arr.push(this.proflie.age)
Arr.push("years old</p>")
return arr.join('')
//模板文件
var template = '<p>Hello, my name is <%this.name%>. I\'m <%this.profile.age%> years old.</p>'; 
//數(shù)據(jù)
var data = { 
               name: "zyn", 
               profile: { age: 29 } 
            }

var re = /<%([^%>]+)?%>/g, 
    code = 'var Arr=[];\n',//code保存函數(shù)體
    cursor = 0;//cursor游標(biāo)告訴我們當(dāng)前解析到了模板的哪個(gè)位置,我們需要它來遍歷整個(gè)模板字符串

//函數(shù)add,負(fù)責(zé)將解析出來的代碼行添加到變量code中去
//特別注意:把code包括的雙引號(hào)字符進(jìn)行轉(zhuǎn)義,否則代碼出錯(cuò)
/*
var func = new Function('x',"console.log(x+\"1\")")
func(1)
*/
var add = function(line){
    //轉(zhuǎn)義
    code += 'arr.push("' + line.relpace(/"/g,'\\"') + '");\n'
}
while(match = regex.exec(tpl)){   
    add(tpl.slice(cursor,match.index))
    add(match[1])
    cursor = match.index + match[0].length
}

第一次while循環(huán)時(shí)
match=[ 0:“<%this.name%>", 
1:"this.name", 
index:21, 
input:"<p>Hello, my name is<%this.name%>.I'm<%this.profile.age%>years old.</p>",
length:2 
] 
=>tpl.slice(cursor,match.index) //<p>Hello, my name is
add(tpl.slice(cursor,match.index)) 
code=
"
var Arr=[];
Arr.push("<p>Hello, my name is ");
"
match[1] =>"this.name"
cursor = match.index + match[0].length //就是<%this.name%>最后一位的位置
然后依次類推循環(huán)

得到

var Arr[]; 
Arr.push("<p>Hello, my name is "); Arr.push("this.name"); 
Arr.push(". I'm "); 
Arr.push("this.profile.age") 
Arr.push("years old </p>") 
return Arr.join("") <p>Hello, my name is <%this.name%>. I'm <%this.profile.age%> years old.</p> 

this.name和this.profile.age不應(yīng)該有引號(hào)

var add = function(line,js){//改變參數(shù),占位符的內(nèi)容和布爾值做參數(shù)
    //轉(zhuǎn)義
    js?code += 'arr.push(' + line + ');\n':// 改動(dòng)1
       code += 'arr.push("' + line.relpace(/"/g,'\\"') + '");\n'
}
while(match = regex.exec(tpl)){   
    add(tpl.slice(cursor,match.index))
    add(match[1],true) //改動(dòng)2
    cursor = match.index + match[0].length
}
 }
改動(dòng)1:三目運(yùn)算,如果是js 就執(zhí)行 code += 'Arr.push(' + line + ');\n' 否則執(zhí)行 code += 'Arr.push("' + line.replace(/"/g, '\\"') + '");\n';

改動(dòng)2:add(match[1],true);告訴函數(shù)add這次傳入的是js.

剩下來是創(chuàng)建函數(shù)并執(zhí)行它

return new Function(code.replace(/[\r\t\n]/g,'')).apply(data);

不需要顯式的傳參數(shù)給函數(shù),使用apply方法來調(diào)用它,它會(huì)自動(dòng)設(shè)定函數(shù)執(zhí)行的上下文,this.name,這里this指向data對(duì)象

  • 當(dāng)條件判斷和循環(huán)時(shí)
var template = 'My skills:' + '<%for(var index in this.skills) {%>' + '<a href=""><%this.skills[index]%></a>' + '<%}%>'; var skills= ["js", "html", "css"] 

如果使用字符串拼接的話,代碼就應(yīng)該是下面的樣子:

var temolate='My skills:' + 
for(var index in this.skills) { +
'<a href="">' + 
this.skills[index] +
'</a>' +
}

這里會(huì)產(chǎn)生一個(gè)異常,Uncaught SyntaxError: Unexpected token for。如果我們調(diào)試一下,把code變量打印出來,我們就能發(fā)現(xiàn)問題所在。

var Arr=[]; 
Arr.push("My skills:"); 
Arr.push(for(var index in this.skills) {); Arr.push("<a href=\"\">"); 
Arr.push(this.skills[index]); 
Arr.push("</a>"); 
Arr.push(}); 
Arr.push(""); 
return Arr.join("");

帶for循環(huán)的那一行不能直接放到數(shù)組里,而是作為腳本的一部分直接運(yùn)行,所以把內(nèi)容添加到code變量之前還要做一個(gè)判斷

最終代碼

var templateEngine = function(html,option){
    var re = /<%([^%>]+)?%>/g, 
    reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, //.*任意字符一次或多次
    code = 'var r=[];\n', 
    cursor = 0;
    var add = function(line,js){
        js?(code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n'):
        (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
        return add
    }
    while(match = re.exec(html)){
        add(html.slice(cursor,match.index))
        add(match[1],true)
        cursor = match.index + match[0].length
    }
    add(html.substr(cursor,html.length - cursor)) //substr(start,length)
    code += 'return r.join("")'
    return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
}

github代碼地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容