探索micro templating源碼 2018-04-22

這兩天在補(bǔ)以前沒(méi)時(shí)間看的內(nèi)容贺拣,這個(gè)micro template是一直落下的驻债,今天把代碼看完就開(kāi)始寫(xiě)讀后感了唆涝,助長(zhǎng)一下記憶脱羡。

源碼只有這么點(diǎn)萝究,精華就在于new Function中那一段字符串免都。

通篇理解就是把找到模板,轉(zhuǎn)成字符串帆竹,再通過(guò)with將傳入的dataObject绕娘,做各種字符串替換和拆分組合,生成一段新的有數(shù)據(jù)的字符串栽连。

流程:
Template String --> Template Function --> Template HTML险领。

下面來(lái)解讀一下這段源碼:

(function() {
    let cache = {};
    this.tmpl = function tmpl(str, data) {
        let fn = !/\W/.test(str) ? 
          cache[str] = cache[str] || 
            tmpl(document.getElementById(str).innerHTML) :
            
        new Function('obj', 
          "var p = [], print=function(){p.push.apply(p,arguments);};" + 
          "with(obj) {p.push('" +
          str
            .replace(/[\r\t\n]/g, " ")
            .split("<%").join("\t")
            .replace(/((^|%>)[^\t]*)"/g, "$1\r")
            .replace(/\t=(.*?)%>/g, "',$1,'")
            .split("\t").join("');")
            .split("%>").join("p.push('")
            .split("\r").join("\\'")
            
        + "');}return p.join('');");
        console.log(fn)
        return data ? fn(data) : fn;
    }
    let results = document.getElementById("results");
    results.innerHTML = tmpl("item_tmpl", undefined);
})();

第一行,新建一個(gè)緩存對(duì)象:

let cache = {};

第二行秒紧,聲明一個(gè)tmpl函數(shù):

this.tmpl = function tmpl(str, data) {
  // something code...
}

tmpl函數(shù)里面:

let fn = !/\W/.test(str) ? 
          cache[str] = cache[str] || 
            tmpl(document.getElementById(str).innerHTML) : 
            new Function('obj',  'str');

其實(shí)tmpl只有一行代碼绢陌,就一條三元表達(dá)式,這就是傳說(shuō)中的一行代碼寫(xiě)出花來(lái)的意思吧熔恢。

核心代碼就在new Function第2個(gè)參數(shù)脐湾,后面會(huì)解析,現(xiàn)在先一步一步來(lái)叙淌。

首先這里會(huì)判斷tmpl函數(shù)傳入的參數(shù)秤掌,首先會(huì)接受頁(yè)面上一個(gè)標(biāo)簽的id:

<script type="text/template" id="item_tmpl">
  <!-- 模板內(nèi)容 -->
</script>

<script>
  let cache = {};
  this.tmpl = function tmpl(str, data) {
    let fn = !/\W/.test(str) ? 
          cache[str] = cache[str] || 
            tmpl(document.getElementById(str).innerHTML) : 
            new Function('obj',  'str');
  }
  // 傳入id名
  tmpl("item_tmpl", undefined); 
</script>

這里的代碼從最下面一行往上看,執(zhí)行 tmpl函數(shù) 后傳入的 item_tmpl鹰霍,接下來(lái)回到剛剛的三元表達(dá)式:

let str = 'item_tmpl';
let fn = !/\W/.test(str) ? 
          cache[str] = cache[str] || 
            tmpl(document.getElementById(str).innerHTML) : 
            new Function('obj',  'str');

拆分出來(lái)看就是這個(gè)狀態(tài)闻鉴,正則表達(dá)式 !/\W/.test(str) 先判斷str中有沒(méi)有非單詞字符串,也就是 [\W] === [^A-Za-z0-9]茂洒。

很好孟岛,一開(kāi)始傳入的 "item_tmpl" 字符串是沒(méi)有的,所以 !/\W/.test(str) 得到的是true督勺,進(jìn)入true的代碼段:

let str = "item_tmpl";
// 剛剛?cè)磉_(dá)式為true的代碼段
cache[str] = cache[str] || tmpl(document.getElementById(str).innerHTML)

這里會(huì)先檢測(cè)cache對(duì)象有沒(méi)有這個(gè)id的緩存渠羞,如果沒(méi)有就再執(zhí)行一次 tmpl函數(shù),至于或后邊的再次執(zhí)行應(yīng)該都看懂了吧玷氏。

沒(méi)錯(cuò)我們又回到了tmpl函數(shù)堵未,但是這一次的str不再是 "item_tmpl" 了。而是這個(gè)<script>下的內(nèi)容盏触,先keep住我們從三元表達(dá)式執(zhí)行tmpl這一段的記憶渗蟹,先看一下假設(shè)的模板:

<!-- 假設(shè)script template中有這樣一段內(nèi)容 -->
<script type="text/html" id="item_tmpl">
  <div id="<%=id%>" class="<%=(i % 2 == 1 ? " even" : "")%>">
    <div class="grid_1 alpha right">
      <img class="righted" src="<%=profile_image_url%>"/>
    </div>
    <div class="grid_6 omega contents">
      <p><b><a href="/<%=from_user%>"><%=from_user%></a>:</b> <%=text%></p>
    </div>
  </div>
</script>  

看完這里,回到剛剛keep住的記憶赞辩,我們又回來(lái)了 tmpl函數(shù)的三元表達(dá)式:

let fn = !/\W/.test(str) ? 
          cache[str] = cache[str] || 
            tmpl(document.getElementById(str).innerHTML) : 
            new Function('obj',  'str');

但是這一次正則表達(dá)式驗(yàn)證的str不再是true了雌芽,因?yàn)槲覀兊玫降奈谋緝?nèi)容有非單詞字符串,比如 空格辨嗽、換行符 等世落。

所以我們就進(jìn)入false階段 new Function的代碼,接下來(lái)的代碼會(huì)有點(diǎn)冗長(zhǎng)糟需,怕引起不適的話請(qǐng)?jiān)卺t(yī)院病床上連著wifi觀看屉佳。

let str = document.getElementById("item_tmpl");
new Function('obj', 
  "var p = [], print=function(){p.push.apply(p,arguments);};" + 
  "with(obj) {p.push('" +
  str
    .replace(/[\r\t\n]/g, " ")
    .split("<%").join("\t")
    .replace(/((^|%>)[^\t]*)"/g, "$1\r")
    .replace(/\t=(.*?)%>/g, "',$1,'")
    .split("\t").join("');")
    .split("%>").join("p.push('")
    .split("\r").join("\\'")
    
+ "');}return p.join('');");

是的谷朝,很蛋疼,這一串跟亂碼一樣的代碼武花。其實(shí)認(rèn)真一看就是用new的方式創(chuàng)建函數(shù):

function(obj) {
  var p = [];
  var print = function(){
    p.push.apply(p,arguments);
  };
  with(obj) {
    let str1 = str
        .replace(/[\r\t\n]/g, " ")
        .split("<%").join("\t")
        .replace(/((^|%>)[^\t]*)"/g, "$1\r")
        .replace(/\t=(.*?)%>/g, "',$1,'")
        .split("\t").join("');")
        .split("%>").join("p.push('")
        .split("\r").join("\\'")

    p.push(str1);
  }
  return return p.join('');
}

好的圆凰,能看到這里的都是挺有耐心的,我們這里就直奔with部分吧体箕,就這里是重點(diǎn):

let str1 = str
  .replace(/[\r\t\n]/g, " ")
  .split("<%").join("\t")
  .replace(/((^|%>)[^\t]*)"/g, "$1\r")
  .replace(/\t=(.*?)%>/g, "',$1,'")
  .split("\t").join("');")
  .split("%>").join("p.push('")
  .split("\r").join("\\'")

其實(shí)這一段說(shuō)是沒(méi)什么好說(shuō)的了专钉,主要就是用正則做替換,把 <% %> 改成用逗號(hào)和變量分割的形式累铅,再push進(jìn)p數(shù)組里跃须。

最后就是合并數(shù)組了:

return p.join('');

這個(gè)時(shí)候你只需要將script模板,和obj數(shù)據(jù)傳進(jìn)tmpl中就搞定了娃兽。


OK菇民,殺青了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末换薄,一起剝皮案震驚了整個(gè)濱河市玉雾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌轻要,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垦缅,死亡現(xiàn)場(chǎng)離奇詭異冲泥,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)壁涎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)凡恍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人怔球,你說(shuō)我怎么就攤上這事嚼酝。” “怎么了竟坛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵闽巩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我担汤,道長(zhǎng)涎跨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任崭歧,我火速辦了婚禮隅很,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘率碾。我一直安慰自己叔营,他們只是感情好屋彪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著绒尊,像睡著了一般畜挥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上垒酬,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天砰嘁,我揣著相機(jī)與錄音,去河邊找鬼勘究。 笑死矮湘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的口糕。 我是一名探鬼主播缅阳,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼景描!你這毒婦竟也來(lái)了十办?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤超棺,失蹤者是張志新(化名)和其女友劉穎向族,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體棠绘,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡件相,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氧苍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夜矗。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖让虐,靈堂內(nèi)的尸體忽然破棺而出紊撕,到底是詐尸還是另有隱情,我是刑警寧澤赡突,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布对扶,位于F島的核電站,受9級(jí)特大地震影響麸俘,放射性物質(zhì)發(fā)生泄漏辩稽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一从媚、第九天 我趴在偏房一處隱蔽的房頂上張望逞泄。 院中可真熱鬧,春花似錦、人聲如沸喷众。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)到千。三九已至昌渤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憔四,已是汗流浹背膀息。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留了赵,地道東北人潜支。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像柿汛,于是被迫代替她去往敵國(guó)和親冗酿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345