国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[譯] 只有 20 行的 JavaScript 模板引擎

leon / 2574人閱讀

摘要:原文鏈接譯者吐槽只收藏不點贊都是耍流氓前言我仍舊在為我的預(yù)處理器進(jìn)行開發(fā)工作。它原本是一個預(yù)處理器,但之后它擴展成為了預(yù)處理器,很快它將支持到的轉(zhuǎn)換。

原文鏈接:JavaScript template engine in just 20 lines

(譯者吐槽:只收藏不點贊都是耍流氓)

前言

我仍舊在為我的JS預(yù)處理器AbsurdJS進(jìn)行開發(fā)工作。它原本是一個CSS預(yù)處理器,但之后它擴展成為了CSS/HTML預(yù)處理器,很快它將支持JS到CSS/HTML的轉(zhuǎn)換。它就像一個模板引擎一樣能夠生成HTML代碼,也就是說它能夠用數(shù)據(jù)填充模板當(dāng)中的標(biāo)識片段。

因此,我希望去寫一個可以滿足我當(dāng)前需求的模板引擎。AbsurdJS主要作為NodeJS的模塊使用,但同時它也可以在客戶端使用。為了這個目的,我無法使用市面上已經(jīng)存在的模板引擎,因為它們幾乎全都依賴于NodeJS,并且難以在瀏覽器中使用。我需要一個更小,純JS寫成的模板引擎。我瀏覽了這篇由John Resig寫的博客,似乎這正是我需要的東西。我把當(dāng)中的代碼稍作修改,并且濃縮到了20行。

這段代碼的運行原理非常有趣,我將在這篇文章中一步一步為大家展示John的wonderful idea。

1、提取標(biāo)識片段

這是我們在開始的時候?qū)⒁@得的東西:

var TemplateEngine = function(tpl, data) {
    // magic here ...
}
var template = "

Hello, my name is <%name%>. I"m <%age%> years old.

"; console.log(TemplateEngine(template, { name: "Krasimir", age: 29 }));

一個簡單的函數(shù),傳入模板數(shù)據(jù)作為參數(shù),正如你所想象的,我們想要得到以下的結(jié)果:

Hello, my name is Krasimir. I"m 29 years old.

我們要做的第一件事就是獲取模板中的標(biāo)識片段<%...%>,然后用傳入引擎中的數(shù)據(jù)去填充它們。我決定用正則表達(dá)式去完成這些功能。正則不是我的強項,所以大家將就一下,如果有更好的正則也歡迎向我提出。

var re = /<%([^%>]+)?%>/g;

我們將會匹配所有以<%開頭以%>結(jié)尾的代碼塊,末尾的g(global)表示我們將匹配多個。有許多的方法能夠用于匹配正則,但是我們只需要一個能夠裝載字符串的數(shù)組就夠了,這正是exec所做的工作:

var re = /<%([^%>]+)?%>/g;
var match = re.exec(tpl);

在控制臺console.log(match)可以看到:

[
    "<%name%>",
    " name ", 
    index: 21,
    input: 
    "

Hello, my name is <%name%>. I"m <%age%> years old.

" ]

我們?nèi)〉昧苏_的匹配結(jié)果,但正如你所看到的,只匹配到了一個標(biāo)識片段<%name%>,所以我們需要一個while循環(huán)去取得所有的標(biāo)識片段。

var re = /<%([^%>]+)?%>/g, match;
while(match = re.exec(tpl)) {
    console.log(match);
}

運行,發(fā)現(xiàn)所有的標(biāo)識片段已經(jīng)被我們獲取到了。

2、數(shù)據(jù)填充與邏輯處理

在獲取了標(biāo)識片段以后,我們就要對它們進(jìn)行數(shù)據(jù)的填充。使用.replace方法就是最簡單的方式:

var TemplateEngine = function(tpl, data) {
    var re = /<%([^%>]+)?%>/g, match;
    while(match = re.exec(tpl)) {
        tpl = tpl.replace(match[0], data[match[1]])
    }
    return tpl;
}

data = {
    name: "Krasimir Tsonev",
    age: 29
}

OK,正常運行。但很明顯這并不足夠,我們當(dāng)前的數(shù)據(jù)結(jié)構(gòu)非常簡單,但實際開發(fā)中我們將面臨更復(fù)雜的數(shù)據(jù)結(jié)構(gòu):

{
    name: "Krasimir Tsonev",
    profile: { age: 29 }
}

出現(xiàn)錯誤的原因,是當(dāng)我們在模板中輸入<%profile.age%>的時候,我們得到的data["profile.age"]是undefined的。顯然.replace方法是行不通的,我們需要一些別的方法把真正的JS代碼插入到<%和%>當(dāng)中,就像以下栗子:

var template = "

Hello, my name is <%this.name%>. I"m <%this.profile.age%> years old.

";

這看似不可能完成?John使用了new Function,即通過字符串去創(chuàng)建一個函數(shù)的方法去完成這個功能。舉個栗子:

var fn = new Function("arg", "console.log(arg + 1);");
fn(2); // 輸出 3

fn是個真正的函數(shù),它包含一個參數(shù),其函數(shù)體為console.log(arg + 1)。以上代碼等價于下列代碼:

var fn = function(arg) {
    console.log(arg + 1);
}
fn(2); // 輸出 3

通過new Function,我們得以通過字符串去創(chuàng)建一個函數(shù),這正是我們所需要的。在創(chuàng)建這么一個函數(shù)之前,我們需要去構(gòu)造這個它的函數(shù)體。該函數(shù)體應(yīng)當(dāng)返回一個最終拼接好了的模板。沿用前文的模板字符串,想象一下這個函數(shù)應(yīng)當(dāng)返回的結(jié)果:

return 
"

Hello, my name is " + this.name + ". I"m " + this.profile.age + " years old.

";

顯然,我們把模板分成了文本和JS代碼。正如上述代碼,我們使用了簡單的字符串拼接的方式去獲取最終結(jié)果,但是這個方法無法100%實現(xiàn)我們的需求,因為之后我們還要處理諸如循環(huán)之類的JS邏輯,像這樣:

var template = 
"My skills:" + 
"<%for(var index in this.skills) {%>" + 
"<%this.skills[index]%>" +
"<%}%>";

如果使用字符串拼接,結(jié)果將會變成這樣:

return
"My skills:" + 
for(var index in this.skills) { +
"" + 
this.skills[index] +
"" +
}

理所當(dāng)然這會報錯。這也是我決定參照J(rèn)ohn的文章去寫邏輯的原因——我把所有的字符串都push到一個數(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("");

下一步邏輯就是整理得到的每一行代碼以便生成函數(shù)。我們已經(jīng)從模板中提取出了一些信息,知道了標(biāo)識片段的內(nèi)容和位置,所以我們可以通過一個指針變量(cursor)去幫助我們?nèi)〉米罱K的結(jié)果:

var TemplateEngine = function(tpl, data) {
    var re = /<%([^%>]+)?%>/g,
        code = "var r=[];
",
        cursor = 0, match;
    var add = function(line) {
        code += "r.push("" + line.replace(/"/g, """) + "");
";
    }
    while(match = re.exec(tpl)) {
        add(tpl.slice(cursor, match.index));
        add(match[1]);
        cursor = match.index + match[0].length;
    }
    add(tpl.substr(cursor, tpl.length - cursor));
    code += "return r.join("");"; // <-- return the result
    console.log(code);
    return tpl;
}
var template = "

Hello, my name is <%this.name%>. I"m <%this.profile.age%> years old.

"; console.log(TemplateEngine(template, { name: "Krasimir Tsonev", profile: { age: 29 } }));

變量code以聲明一個數(shù)組為開頭,作為整個函數(shù)的函數(shù)體。正如我所說的,指針變量cursor表示我們正處于模板的哪個位置,我們需要它去遍歷所有的字符串,跳過填充數(shù)據(jù)的片段。另外,add函數(shù)的任務(wù)是把字符串插入到code變量中,作為構(gòu)建函數(shù)體的過程方法。這里有一個棘手的地方,我們需要跳過標(biāo)識符<%%>,否則當(dāng)中的JS腳本將會失效。如果我們直接運行上述代碼,結(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("");

呃……這不是我們想要的。this.namethis.profile.age不應(yīng)該帶引號。我們改進(jìn)一下add函數(shù):

var add = function(line, js) {
    js? code += "r.push(" + line + ");
" :
        code += "r.push("" + line.replace(/"/g, """) + "");
";
}
var match;
while(match = re.exec(tpl)) {
    add(tpl.slice(cursor, match.index));
    add(match[1], true); // <-- say that this is actually valid js
    cursor = match.index + match[0].length;
}

標(biāo)識片段中的內(nèi)容將通過一個boolean值進(jìn)行控制。現(xiàn)在我們得到了一個正確的函數(shù)體:

var r=[];
r.push("

Hello, my name is "); r.push(this.name); r.push(". I"m "); r.push(this.profile.age); return r.join("");

接下來我們要做的就是生成這個函數(shù)并且運行它。在這個模板引擎的末尾,我們用以下代碼去代替直接返回一個tpl對象:

return new Function(code.replace(/[
	
]/g, "")).apply(data);

我們甚至不需要向函數(shù)傳遞任何的參數(shù),因為apply方法已經(jīng)為我們完整了這一步工作。它自動設(shè)置了作用域,這也是為什么this.name可以運行,this指向了我們的data。

3、代碼優(yōu)化

大致上已經(jīng)完成了。最后一件事情,我們需要支持更多復(fù)雜的表達(dá)式,像if/else表達(dá)式和循環(huán)等。讓我們用同樣的例子去嘗試運行下列代碼:

var template = 
"My skills:" + 
"<%for(var index in this.skills) {%>" + 
"<%this.skills[index]%>" +
"<%}%>";
console.log(TemplateEngine(template, {
    skills: ["js", "html", "css"]
}));

結(jié)果將會報錯,錯誤為Uncaught SyntaxError: Unexpected token for。仔細(xì)觀察,通過code變量我們可以找出問題所在:

var r=[];
r.push("My skills:");
r.push(for(var index in this.skills) {);
r.push("");
r.push(this.skills[index]);
r.push("");
r.push(});
r.push("");
return r.join("");

包含著for循環(huán)的代碼不應(yīng)該被push到數(shù)組當(dāng)中,而是直接放在腳本里面。為了解決這個問題,在把代碼push到code變量之前我們需要多一步的判斷:

var re = /<%([^%>]+)?%>/g,
    reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g,
    code = "var r=[];
",
    cursor = 0;
var add = function(line, js) {
    js? code += line.match(reExp) ? line + "
" : "r.push(" + line + ");
" :
        code += "r.push("" + line.replace(/"/g, """) + "");
";
}

我們添加了一個新的正則。這個正則的作用是,如果一段JS代碼以if, for, else, switch, case, break, |開頭,那它們將會直接添加到函數(shù)體中;如果不是,則會被push到code變量中。下面是修改后的結(jié)果:

var r=[];
r.push("My skills:");
for(var index in this.skills) {
r.push("");
r.push(this.skills[index]);
r.push("");
}
r.push("");
return r.join("");

理所當(dāng)然的正確執(zhí)行啦:

My skills:jshtmlcss

接下來的修改會給予我們更強大的功能。我們可能會有更加復(fù)雜的邏輯會放進(jìn)模板中,像這樣:

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 }));

進(jìn)行過一些細(xì)微的優(yōu)化之后,最終的版本如下:

var TemplateEngine = function(html, options) {
    var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = "var r=[];
", cursor = 0, match;
    var add = function(line, js) {
        js? (code += line.match(reExp) ? line + "
" : "r.push(" + line + ");
") :
            (code += line != "" ? "r.push("" + line.replace(/"/g, """) + "");
" : "");
        return add;
    }
    while(match = re.exec(html)) {
        add(html.slice(cursor, match.index))(match[1], true);
        cursor = match.index + match[0].length;
    }
    add(html.substr(cursor, html.length - cursor));
    code += "return r.join("");";
    return new Function(code.replace(/[
	
]/g, "")).apply(options);
}

優(yōu)化后的代碼甚至少于15行。

后記(譯者注)

這是我第一次完整地翻譯文章,語句多有錯漏還請多多諒解,今后將繼續(xù)努力,爭取把更多優(yōu)質(zhì)的文章翻譯分享。

由于對前端的框架、模板引擎一類的工具特別感興趣,非常希望能夠?qū)W習(xí)當(dāng)中的原理,于是乎找了個相對簡單的模板引擎開刀進(jìn)行研究,google后看到了這篇文章覺得非常優(yōu)秀,一步步講解生動且深入,代碼經(jīng)過本人測試均能正確得到文章描述的結(jié)果。

模板引擎有多種設(shè)計思路,本文僅僅為其中的一種,其性能等參數(shù)還有待測試和提高,僅供學(xué)習(xí)使用。
謝謝大家~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79633.html

相關(guān)文章

  • JavaScript 究竟是如何工作的?(第一部分)

    摘要:文章的第二部分涵蓋了內(nèi)存管理的概念,不久后將發(fā)布。的標(biāo)準(zhǔn)化工作是由國際組織負(fù)責(zé)的,相關(guān)規(guī)范被稱為或者。隨著分析器和編譯器不斷地更改字節(jié)碼,的執(zhí)行性能逐漸提高。 原文地址:How Does JavaScript Really Work? (Part 1) 原文作者:Priyesh Patel 譯者:Chor showImg(https://segmentfault.com/img...

    Youngdze 評論0 收藏0
  • 】當(dāng)不使用JavaScript框架時

    摘要:在其他方面,我們只需要考慮針對特定任務(wù)時所使用框架的成本。當(dāng)我們必須使用或不應(yīng)該使用框架時我強烈主張要了解編寫某個工具的目的。 非常有價值的建議:哪些框架是合理的,哪些并不合理。 作者:Tod Hansmann 來源:https://opensource.com/articl...翻譯:瘋狂的技術(shù)宅說明:本專欄文章首發(fā)于公眾號:jingchengyideng 。 showImg(htt...

    blair 評論0 收藏0
  • 只有20Javascript代碼!手把手教你寫一個頁面模板引擎

    摘要:整個引擎實現(xiàn)只有不到行代碼。不知道你有木有聽說過一個基于的頁面預(yù)處理器,叫做。最初我只是打算寫一個的預(yù)處理器,不過后來擴展到了和,可以用來把代碼轉(zhuǎn)成和代碼。最后一個改進(jìn)可以使我們的模板引擎更為強大。 導(dǎo)讀:AbsurdJS 作者寫的一篇教程,一步步教你怎樣用 Javascript 實現(xiàn)一個純客戶端的模板引擎。整個引擎實現(xiàn)只有不到 20 行代碼。如果你能從頭看到尾的話,還能有不少收獲的。...

    Luosunce 評論0 收藏0
  • 正則表達(dá)式

    摘要:最全正則表達(dá)式總結(jié)驗證號手機號中文郵編身份證地址等是正則表達(dá)式的縮寫,作用是對字符串執(zhí)行模式匹配。學(xué)習(xí)目標(biāo)了解正則表達(dá)式語法在中使用正則表達(dá)式在中使 JS高級技巧 本篇是看的《JS高級程序設(shè)計》第23章《高級技巧》做的讀書分享。本篇按照書里的思路根據(jù)自己的理解和經(jīng)驗,進(jìn)行擴展延伸,同時指出書里的一些問題。將會討論安全的類型檢測、惰性載入函數(shù)、凍結(jié)對象、定時器等話題。1. 安全的類型檢測...

    yibinnn 評論0 收藏0
  • 搞懂JavaScript的Function.prototype.bind[]

    摘要:搞懂的譯可能是初學(xué)的人最不關(guān)心的函數(shù),當(dāng)你意識到需要保持在其他函數(shù)中的上下文,實際上你需要的是。這就是問題所在。整合事件綁定和一個重大提高就是,和等等。然而,并沒有原生添加事件到多個節(jié)點的方式。能力有限,如有疑問,紕漏,速指出,感謝你 搞懂JavaScript的Function.prototype.bind[譯] Ben Howdle binding可能是初學(xué)Javascript的人最...

    Pandaaa 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<