大佬的github
A simple template engine,only 50 rows.
學習目的
- 與大佬約飯有一種約會的感覺(平淡臉.jpg),偶然聽從大佬寫了一個簡單的模板編譯工具,就興致勃勃的拿下來看看。
- 最近看vue原始碼一腦袋包,在一路堵堵的情況下,也算是看懂了watcher observe dep三者的關係,接下來的render部分,忙完這陣子業務再去看把 哭臉.jpg。
模板demo
<body>
<div id="root"></div>
<script id="tplContent" type="text/html">
<ul>
<% for(var i=0; i < data.length; i++){
var item = data[i];
if(item.age < 30){%>
<li>我的名字是<%=item.name%>,我的年齡是<%=item.age%></li>
<%}else{%>
<li>my name is <%=item.name%>,my age is a sercet.</li>
<%}%>
<% } %>
</ul>
</script>
<script src="../build/mini-tpl.min.js"></script>
<script>
var data = [{ name: 'tom', age: 12 }, { name: 'lily', age: 24 }, { name: 'lucy', age: 55 }];
var content = document.getElementById('tplContent').innerHTML;
var result = miniTpl(content, data);
document.getElementById('root').innerHTML = result;
</script>
</body>
複製程式碼
在
原始碼
var testLock = true
function cos(e) {
if (testLock) {
console.log(e)
}
}
function factory() {
function render(content, data) {
data = data || {}; //
var list = ['var tpl = "";'];
var codeArr = transform(content);
for (var i = 0, len = codeArr.length; i < len; i++) {
var item = codeArr[i];
if (item.type == 1) { //html標籤
list.push(item.txt);
} else if (item.type == 2) { //需要賦值的語句
var txt = "tpl+=" + item.txt + ";";
list.push(txt);
} else {
var txt = 'tpl+="' + item.txt.replace(/"/g, '\\"') + '";';
list.push(txt);
}
}
list.push("return tpl;");
cos(list.join("\n"))
cos(data)
cos(new Function("data", list.join("\n"))(data))
return new Function("data", list.join("\n"))(data);
}
function transform(content) { //賦值的過程,並輸出正則的切割陣列
cos(content.split(''))
var arr = [];
var reg = /<%([\s\S]*?)%>/g;
var match;
var nowIndex = 0;
var i = 0
while (match = reg.exec(content)) { // 匹配多少個,就push多少次*2
cos(match)
appendTxt(arr, content.substring(nowIndex, match.index)); //index匹配初始到的位置
var item = { //預設是標籤
type: 1,
txt: match[1] //匹配的內部內容,不帶<%這個的
};
if (match[1].substr(0, 1) == "=") { //如果開頭是=,則表示是賦值語句
item.type = 2;
item.txt = item.txt.substr(1);
}
arr.push(item);
nowIndex = match.index + match[0].length;
}
cos(content.substr(nowIndex))
appendTxt(arr, content.substr(nowIndex)); //將%>後面的閉合標籤push進去
cos(arr)
return arr;
}
function appendTxt(list, content) { //list是一個引用型別,修改的list是傳入的list的 ,content也是
content = content.replace(/\r?\n/g, "\\n");
list.push({
txt: content
});
}
return render;
}
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof exports === "object") {
var mo = factory();
mo.__esModule = true;
mo["default"] = mo;
module.exports = mo;
} else {
root.miniTpl = factory();
}
})(this, factory);
複製程式碼
先看一下這一段
入口
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof exports === "object") {
var mo = factory();
mo.__esModule = true;
mo["default"] = mo;
module.exports = mo;
} else {
root.miniTpl = factory();
}
})(this, factory);
複製程式碼
將上下文this與建構函式factory放入自執行函式,進行環境判斷,這裡我們進入第三個判斷
root.miniTpl = factory() //root即上下文,這裡指window
複製程式碼
factory
factory最終會返回一個render函式,在render函式中先對data進行初始化賦值,list是用來存拼接最終的函式。接下來會執行transform函式,這個函式是用來格式化我們傳入的innerHTML(傳入的是一個字串)
transform
var reg = /<%([\s\S]*?)%>/g
複製程式碼
定義了一個正則匹配我們的識別符號<%和%>用exec函式進行匹配,此函式會返回需要用到的2個引數
- 去掉<%和%>的內容
- 出現的下標位置
每個匹配到的會記錄最新的分割的下標,以及起始下標(nowIndex),在appendTxt函式中,將不在<% %>中的欄位也push到arr中去儲存起來,最終會得到<% %>閉合標籤數量*2+1個length的arr。 這個+1是因為最後一個%>外面還有一個閉合標籤
appendTxt
會對傳入的引數進行修改,因為傳入引數的型別是引用型別,因此會同步修改
字串函式拼接
得到上文返回的codeArr =arr
for (var i = 0, len = codeArr.length; i < len; i++) {
var item = codeArr[i];
if (item.type == 1) { //html標籤
list.push(item.txt);
} else if (item.type == 2) { //需要賦值的語句
var txt = "tpl+=" + item.txt + ";";
list.push(txt);
} else {
var txt = 'tpl+="' + item.txt.replace(/"/g, '\\"') + '";';
list.push(txt);
}
}
複製程式碼
type =1指的是普通的函式語句,例如for() else{}
type =2指的是賦值語句,例如 =
else則指的普通html標籤
最後不上一句
"return tpl;"
複製程式碼
new Function
此刻拼接的字串是
var tpl = "";
tpl+="\n <ul>\n ";
for(var i=0; i < data.length; i++){
var item = data[i];
if(item.age < 30){
tpl+="\n <li>我的名字是\n ";
tpl+=item.name;
tpl+=",我的年齡是\n ";
tpl+=item.age;
tpl+="\n </li>\n ";
}else{
tpl+="\n <li>my name is\n ";
tpl+=item.name;
tpl+=",my age is a sercet.</li>\n ";
}
tpl+="\n ";
}
tpl+="\n </ul>\n ";
return tpl;
複製程式碼
接下來進行到
return new Function("data", list.join("\n"))(data)
複製程式碼
這個作用和eval類似,先進行字串操作,再執行函式操作,強就強在可以傳入引數()()也表明是一個自執行函式,將結果往上返回
最後
將得到的string賦值給innerHTML 進行瀏覽器渲染,就得到想要的html。