template模板
一個簡單的字符串模板
var template = (function() {
var cache = {};
return function(obj, str) {
if (!typeof str === "string") {
return;
}
var compile = cache[str]
if (!cache[str]) {
var template = str.replace(/<%=\s*([^%>]+)\s*%>/g, function() {
var k = arguments[1]
var tm = "';" + k + " tmp+='"
console.log("tm", tm)
return tm;
})
.replace(/\$\{\s*([^\}]+)\s*\}/g, function() {
var k = arguments[1]
var tm = "' +" + k+ "+'";
return tm;
})
template = "var tmp = \"\"; with(obj){tmp ='" + template + "';}return tmp;"
console.log("template", template)
compile = new Function("obj", template)
}
return compile(obj)
}
})()
使用
var str = "" +
"<div> "+
"<ul> "+
"<%= for(var i=0;i < obj.length; i++) { %> "+
"<li>${obj[i].name}</li> "+
"<%= } %> "+
"</ul> "+
"</div> "
var obj = [
{name: "李飛1"},
{name: "李飛2"},
{name: "李飛3"},
];
var html = template(obj, str);
//生成字符串:"<div> <ul> <li>李飛1</li> <li>李飛2</li> <li>李飛3</li> </ul> </div> "
介紹
-
template函數有兩個參數,第一個是數據,第二個是字符串模板.
-
字符串模板的格式要求
- 要執行的js代碼需要寫在: <%= 這里 %> . 比如: <%= var a = ""; %>
- 從對象里拿的數據寫在: ${ 這里 } . 比如: ${obj.name}
-
如果obj是對象的話,可以直接從對象里拿數據.比如:
var obj = {name: "李飛"}; var str = "hello, ${name}"; var html = template(obj, str); //html 為 "hello,"李飛
-
如果obj是數組的話,不能直接拿,需要把數組便利才能使用.
原理分析
簡單來說就是,把js代碼寫在固定的格式中,用String.replace()方法,獲取到js代碼的字符串.
然后拼成一個函數格式的字符串.再用new Function()用這個函數字符串生成一個函數,去處理
模板里的字符串並返回
知識點
-
String.replace(參數1, 參數2)
- 參數1,可以是字符串也可以是正則表達式,如果是正則表示式.並且有全局匹配g的話,會替換所有的匹配項.如果此時參數2是函數,那么有多少個匹配項,函數就會執行杜少次
- 參數2,可以是字符串,也可以是回調函數.函數的返回值會替代匹配的字符串.而回調函數的參數是一般有四個.可以用arguments來拿;
- 第一個參數是正則匹配到的字符串
- 第二個參數是與正則表達式中子表達式相匹配的字符串.也就是正則中的括號()匹配到的字符串.也即是template函數中我們需要拼接的js字符串.
- 如果子表達式有2個,則這里是第二個.多個以此類推.如果子表達式沒有了.這里是整個被匹配到的字符串所在的length.
- 完整的原始字符串;
- replace函數不改變源字符串.返回值是替換之后的結果
-
new Function(參數1, 參數2, functionBody)
- Function函數的最后一個參數functionBody就是函數字符串.這里的字符串會被變成js代碼成為函數體
- functionBody前面的參數都是最終函數的參數.
-
with語句
var obj = {name: "lifei", age: 18}; with(obj) { var a = name; // "lifei" var age = age; // 18 }
在with語句中可以用變量直接獲取obj對象上的屬性.就好像把window換成了obj一樣.可以直接訪問他的屬性.用在template函數中可以讓拼接的字符串更加的清晰.但是with語句執行緩慢.慎用.
-
用來匹配js代碼的正則表達式: /<%=\s([^%>]+)\s%>/g
- g: 全局匹配
- <%= : 用"<%="開頭
- \s* : 不定數量的空格
- [^%>] : 除了"%>"以外的其他字符
- () : 子表達式
詳解
因為有Function函數的存在.我們可以拼接一個函數字符串來生成函數.對源字符串進行處理.所以我們的主要問題就是用String.replace()拿到模板里的js代碼.再和源字符串一起拼接成一個函數字符串.再用new Function()生成函數,處理字符串.生成最后的結果.
比如:
```
var str = "<span>name</span>"
var obj = {name: "李飛"}
```
這兩個參數我們最后要生成的函數字符串是:
```
"var tmp = \"\"; with(obj){tmp ='<span>name</span>';}return tmp;"
```
```
var template = (function() {
var cache = {};
return function(obj, str) {
//判斷str
if (!typeof str === "string") {
return;
}
//懶加載
var compile = cache[str]
if (!cache[str]) {
var template = str.replace(/<%=\s*([^%>]+)\s*%>/g, function() {
//arguments拿到正則中([^%>]+)匹配到的,js代碼;
var k = arguments[1]
//拼接,這里最麻煩
var tm = "';" + k + " tmp+='"
return tm;
})
//因為js的執行代碼和從對象中取數據的代碼格式不一樣.所以要匹配兩次.
.replace(/\$\{\s*([^\}]+)\s*\}/g, function() {
var k = arguments[1]
var tm = "' +" + k+ "+'";
return tm;
})
//拼接函數代碼
template = "var tmp = \"\"; with(obj){tmp ='" + template + "';}return tmp;"
console.log("template")
//生成函數.
compile = new Function("obj", template)
}
//返回值
return compile(obj)
}
})()