這兩天在補(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菇民,殺青了!