摘要:函數(shù)執(zhí)行后應(yīng)該返回最終編譯的模板。到了這里,我們只需要?jiǎng)?chuàng)建函數(shù)并執(zhí)行它。
上文鏈接翻譯_只需20行代碼創(chuàng)造JavaScript模板引擎(一)
但是這還不夠好,數(shù)據(jù)是非常簡單的對(duì)象,并且很容易使用object["property"]對(duì)象的中括號(hào)語法,去讀取對(duì)象的值。
但在實(shí)踐中,我們用到的數(shù)據(jù)中,可能有復(fù)雜的嵌套對(duì)象。
//嵌套對(duì)象 data = { name: "Krasimir Tsonev", profile: {age:29} }
如果有復(fù)雜的嵌套對(duì)象,就不能用對(duì)象的中括號(hào)語法讀取值了。
所以String.prototype.replace(matchedStr, data["profile.age"]) 就行不通了。
因?yàn)閐ata["profile.age"],每次返回undefined。
//對(duì)象的中括號(hào)語法讀取值 object["property"] var obj = { name: "Shaw", age: 18 } console.log(obj["name"]); //"Shaw" console.log(obj["age"]); // 18 //復(fù)雜的嵌套對(duì)象,就不能用對(duì)象的中括號(hào)語法讀取值了。 var obj = { name: "Shaw", profile: { age: 18 } } console.log(obj["profile.age"]); // undefined
那么,怎么解決這個(gè)問題?
最好的辦法是在模板中<%和%>之間放置真正的JavaScript代碼。
var tpl = "Hello, my name is <%this.name%>. I"m <%this.profile.age%> years old.
";
這怎么可能呢? John使用了new Function()語法, 沒有顯式的聲明函數(shù)。
var fn = new Function("arg", "console.log(arg+1);"); fn(2); // 3 //fn是一個(gè)可以傳入一個(gè)參數(shù)的函數(shù) //fn函數(shù)體內(nèi)的語句,就是 console.log(arg+1); /* 等價(jià)于*/ function fn(arg) { console.log(arg +1); } fn(2); //3
我們可以利用這個(gè)語法,在一行代碼中,定義函數(shù)、參數(shù)和函數(shù)體。這正是我們需要的。
在利用這種語法,創(chuàng)建函數(shù)之前。
我們必須設(shè)計(jì)好,函數(shù)體怎么寫。函數(shù)執(zhí)行后應(yīng)該返回最終編譯的模板。
回想一下,我們經(jīng)常使用的字符竄拼接方法。
"Hello, my name is " + this.name + ". I"m" + this.profile.age + " years old.
";
這說明,我們可以把字符竄模板里面的內(nèi)容拆解,拆解為html和JavaScript代碼。
一般情況下,我們都是使用for循環(huán)去遍歷數(shù)據(jù)。
// 傳入的字符竄模塊 var template = "My Skill:" + "<%for(var index in this.skills) {%>" + "<%this.skills[index]%>" + "<%}%>";
// 預(yù)想的返回結(jié)果 return "My skills:" + for(var index in this.skills) { + "" + this.skills[index] + "" + }
當(dāng)然,這將產(chǎn)生一個(gè)錯(cuò)誤。這就是為什么我決定遵循約翰文章中使用的邏輯,把所有的字符串放到一個(gè)數(shù)組中。
var r = []; r.push("My skills:"); for(var index in this.skills) { r.push(""); r.push(this.skills[index]); r.push(""); } return r.join("");
下一個(gè)邏輯步驟是收集定制生成函數(shù)的不同行。
我們已經(jīng)從模板中提取了一些信息。我們知道占位符的內(nèi)容和它們的位置。所以,通過使用一個(gè)輔助變量(游標(biāo))
function TemplateEngine(tpl, data) { var tplExtractPattern = /<%([^%>]+)?%>/g, code = "var r=[]; ", cursor = 0, match; function addCode(line) { code += "r.push("" + line.replace(/"/g,""") +""); "; } while(match = tplExtractPattern.exec(tpl)) { addCode(tpl.slice(cursor, match.index)); addCode(match[1]); cursor = match.index + match[0].length; } code += "return r.join("");"; console.log(code); return tpl; } var template = "Hello, my name is <%this.name%>. I"m <%this.profile.age%> years old.
"; TemplateEngine(template, { name: "Shaw", profile: { age: 18 } });
變量code保存著函數(shù)體的代碼。
在while循環(huán)語句中,我們也需要變量cursor游標(biāo),告訴我們字符竄slice()方法截取的起始坐標(biāo)和末尾坐標(biāo)。
變量code在while循環(huán)語句中,不斷的拼接。
但是code的最終結(jié)果是
/* var r=[]; r.push("Hello, my name is "); r.push("this.name"); r.push(". I"m "); r.push("this.profile.age"); return r.join("");
Hello, my name is <%this.name%>. I"m <%this.profile.age%> years old.
*/
這不是我們想要的。 "this.name" 和 "this.profile.name" 不應(yīng)該被引號(hào)包裹。
所以我們需要addCode函數(shù)做一個(gè)小小的改動(dòng)
function TemplateEngine(tpl,data){ var tplExtractPattern = /<%([^%>]+)?%>/g, code = "var r=[]; ", cursor = 0, match; function addCode(line, js) { if(js) { code += "r.push(" + line + "); "; } else { code += "r.push("" + line.replace(/"/g,""") + ""); "; } } while(match = tplExtractPattern.exec(tpl)) { addCode(tpl.slice(cursor, match.index)); addCode(match[1], true); cursor = match.index + match[0].length; } code += "return r.join("");"; console.log(code); return tpl; } var template = "Hello, my name is <%this.name%>. I"m <%this.profile.age%> years old.
"; TemplateEngine(template, { name: "Shaw", profile: { age: 18 } });
現(xiàn)在this可以正確指向執(zhí)行對(duì)象了。
var r=[]; r.push("Hello, my name is "); r.push(this.name); r.push(". I"m "); r.push(this.profile.age); return r.join("");
到了這里,我們只需要?jiǎng)?chuàng)建函數(shù)并執(zhí)行它。
在TemplateEngine函數(shù)里把return tpl 替換成
return new Function(code.replace(/[ ]/g, "")).apply(data);
我們不需要傳入?yún)?shù),這里我使用apply()方法,改變了作用域,現(xiàn)在this.name指向了data。
幾乎已經(jīng)完成了。但是我們還需要支持更多JavaScript關(guān)鍵字,比如if/else,循環(huán)流程語句。
讓我們從相同的例子,再次進(jìn)行構(gòu)思。
function TemplateEngine(tpl,data){ var tplExtractPattern = /<%([^%>]+)?%>/g, code = "var r=[]; ", cursor = 0, match; function addCode(line, js) { if(js) { code += "r.push(" + line + "); "; } else { code += "r.push("" + line.replace(/"/g,""") + ""); "; } } while(match = tplExtractPattern.exec(tpl)) { addCode(tpl.slice(cursor, match.index)); addCode(match[1], true); cursor = match.index + match[0].length; } code += "return r.join("");"; console.log(code); return new Function(code.replace(/[ ]/g, "")).apply(data); } var template = "My skill:" + "<%for(var index in this.skills) {%>" + "<%this.skills[index]%>" + "<%}%>"; TemplateEngine(template, { skills: ["js", "html", "css"] }); // Uncaught SyntaxError: Unexpected token for
調(diào)用TemplateEngine(),控制臺(tái)報(bào)錯(cuò)了,Uncaught SyntaxError: Unexpected token for。
在控制臺(tái)打印出,拼接的代碼
var r=[]; r.push("My skill:"); r.push(for(var index in this.skills) {); r.push(""); r.push(this.skills[index]); r.push(""); r.push(}); return r.join("");
帶有for循環(huán)的語句不應(yīng)該被直接放到數(shù)組里面,而是應(yīng)該作為腳本的一部分直接運(yùn)行。所以在把代碼語句添加到code變量之前還要多做一個(gè)判斷。
var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = "var Arr = []; ", cursor = 0; function addCode(line,js) { if(js){ if(line.match(reExp)) { code += line +" "; } else { code += "r.push(" + line + "); "; } } else { code += "r.push("" + line.replace(/"/g,""")) + ""); "; } }
添加一個(gè)新的正則表達(dá)式。它會(huì)判斷代碼中是否包含if、for、else等關(guān)鍵字。
如果有的話就直接添加到腳本代碼中去,否則就添加到數(shù)組中去。
function TemplateEngine(tpl,data){ var tplExtractPattern = /<%([^%>]+)?%>/g, jsExtractReExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = "var arr = []; ", cursor = 0, match; function addCode(line,js) { if(js){ if(line.match(jsExtractReExp)) { code += line +" "; } else { code += "arr.push(" + line + "); "; } } else { code += "arr.push("" + line.replace(/"/g,""") + ""); "; } } while(match = tplExtractPattern.exec(tpl)) { addCode(tpl.slice(cursor, match.index)); addCode(match[1], true); cursor = match.index + match[0].length; } code += "return arr.join("");"; console.log(code); return new Function(code.replace(/[ ]/g, "")).apply(data); } var template = "My skill:" + "<%for(var index in this.skills) {%>" + "<%this.skills[index]%>" + "<%}%>"; TemplateEngine(template, { skills: ["js", "html", "css"] }); /* var arr = []; arr.push("My skill:"); for(var index in this.skills) { arr.push(""); arr.push(this.skills[index]); arr.push(""); } return arr.join(""); */ //"My skill:jshtmlcss"
一切都是正常編譯的 :)。
最后的修改,實(shí)際上給了我們更強(qiáng)大的處理能力。
我們可以直接將復(fù)雜的邏輯應(yīng)用到模板中。例如
var template = "My skills:" + "<%if(this.showSkills) {%>" + "<%for(var index in this.skills) {%>" + "<%this.skills[index]%>" + "<%}%>" + "<%} else {%>" + "none
" + "<%}%>"; console.log(TemplateEngine(template, { skills: ["js", "html", "css"], showSkills: true })); /* var arr = []; arr.push("My skills:"); if(this.showSkills) { arr.push(""); for(var index in this.skills) { arr.push(""); arr.push(this.skills[index]); arr.push(""); } arr.push(""); } else { arr.push("none
"); } return arr.join(""); */ //"My skills:jshtmlcss"
最后,我進(jìn)一步做了一些優(yōu)化,最終版本如下
var TemplateEngine = function(templateStr, data) { var tplStrExtractPattern = /<%([^%>]+)?%>/g, jsKeyWordsExtractPattern = /(^( )?(for|if|else|swich|case|break|{|}))(.*)?/g, code = "var arr = []; ", cursor = 0, match; var addCode = function(templateStr, jsCode) { if(jsCode) { if(templateStr.match(jsKeyWordsExtractPattern)) { code += templateStr + " "; } else { code += "arr.push(" + templateStr + "); "; } } else { code += "arr.push("" + templateStr.replace(/"/g, """) + ""); "; } } while(match = tplStrExtractPattern.exec(templateStr)){ addCode(templateStr.slice(cursor, match.index)); addCode(match[1], true); cursor = match.index + match[0].length; } code += "return arr.join("");"; return new Function(code.replace(/[ ]/g, "")).apply(data); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/98678.html
摘要:置換型模板引擎的優(yōu)點(diǎn)實(shí)現(xiàn)簡單,缺點(diǎn)效率低,無法滿足高負(fù)載的應(yīng)用請(qǐng)求。用途百度詞條模板引擎可以讓網(wǎng)站程序?qū)崿F(xiàn)界面與數(shù)據(jù)分離,業(yè)務(wù)代碼與邏輯代碼的分離,提升開發(fā)效率,良好的設(shè)計(jì)也提高了代碼的復(fù)用性。前端模板的出現(xiàn)使得前后端分離成為可能。 模板引擎 模板引擎-百度詞條 什么是模板引擎?(百度詞條) 模板引擎(這里特指用于Web開發(fā)的模板引擎)是為了使用戶界面與業(yè)務(wù)數(shù)據(jù)分離而產(chǎn)生的,它可以生成...
摘要:翻譯行代碼創(chuàng)造模板引擎一想看博客原文鏈接,請(qǐng)點(diǎn)擊下方一個(gè)非常好用的學(xué)習(xí)正則表達(dá)的網(wǎng)站正則表達(dá)式圖文解說網(wǎng)站譯文事情的起因,我想編寫一個(gè)邏輯簡單的模板引擎,它可以很好滿足我現(xiàn)在的需求。,表示全局匹配。 翻譯_20行代碼創(chuàng)造JavaScript模板引擎(一) 想看博客原文鏈接,請(qǐng)點(diǎn)擊下方 JavaScript template engine in just 20 lines 一個(gè)非常好用...
摘要:電子表格使用語言電子表格是辦公軟件的必備,我們最熟知的是微軟的。文中用框架來實(shí)現(xiàn)一個(gè)簡單的電子表格,所用代碼僅行。 showImg(https://segmentfault.com/img/remote/1460000019770011); 本文原創(chuàng)并首發(fā)于公眾號(hào)【Python貓】,未經(jīng)授權(quán),請(qǐng)勿轉(zhuǎn)載。 原文地址:https://mp.weixin.qq.com/s/Ob... 今天,...
摘要:電子表格使用語言電子表格是辦公軟件的必備,我們最熟知的是微軟的。文中用框架來實(shí)現(xiàn)一個(gè)簡單的電子表格,所用代碼僅行。 showImg(https://segmentfault.com/img/remote/1460000019770011); 本文原創(chuàng)并首發(fā)于公眾號(hào)【Python貓】,未經(jīng)授權(quán),請(qǐng)勿轉(zhuǎn)載。 原文地址:https://mp.weixin.qq.com/s/Ob... 今天,...
摘要:電子表格使用語言電子表格是辦公軟件的必備,我們最熟知的是微軟的。文中用框架來實(shí)現(xiàn)一個(gè)簡單的電子表格,所用代碼僅行。 showImg(https://segmentfault.com/img/remote/1460000019770011); 本文原創(chuàng)并首發(fā)于公眾號(hào)【Python貓】,未經(jīng)授權(quán),請(qǐng)勿轉(zhuǎn)載。 原文地址:https://mp.weixin.qq.com/s/Ob... 今天,...
閱讀 3581·2023-04-26 02:55
閱讀 2849·2021-11-02 14:38
閱讀 4136·2021-10-21 09:39
閱讀 2842·2021-09-27 13:36
閱讀 3943·2021-09-22 15:08
閱讀 2644·2021-09-08 10:42
閱讀 2802·2019-08-29 12:21
閱讀 667·2019-08-29 11:22