之前的项目中用到了artTemplate模板,感觉挺有意思,于是查看相关资料,自己动手写了个简单地template模板插件。虽然会有一些不足,但也是自己的一番心血。主体代码如下
1 /* 2 * 一个简单地template模板语法写法 3 */ 4 !function () { 5 "use strict"; 6 /* 7 *tpl为传入的模板内容,data为数据 8 */ 9 function template(tpl, data) { 10 /*如果传入的是元素,则获取他的innerHTML赋值给他本身*/ 11 if (tpl.tagName) { 12 tpl = tpl.innerHTML; 13 } 14 /*将tpl中的"转义*/ 15 tpl = tpl.replace(/"/g, '\\"'); 16 /*判断使用字符串拼接方式还是使用push方式*/ 17 var isTrim = ''.trim; 18 /*定义正则表达式 19 reg: 匹配分隔符<%%> 20 reg1: 匹配原生js代码 21 */ 22 var reg = /<%([^\n]*)%>/g; 23 var reg1 = /(for|while|do|if|else|switch|case|default|break|continue|{|})/g; 24 /*定义一个空数组的字符串*/ 25 var str = isTrim ? "var arr = [];" : ["var arr = [];"]; 26 /*定义一个初始的下标,下方会需要*/ 27 var index1 = 0; 28 /*定义一个接收正则结果的变量*/ 29 var regResult; 30 while (regResult = reg.exec(tpl)) { 31 if (isTrim) { 32 str += 'arr.push("' + tpl.slice(index1, regResult.index).replace(/\s+/g, ' ') + '");'; 33 if (regResult[1].match(reg1)) { 34 str += regResult[1]; 35 } else { 36 str += 'arr.push(' + regResult[1] + ');'; 37 } 38 } else { 39 str.push('arr.push("'); 40 str.push(tpl.slice(index1, regResult.index).replace(/\s+/g, ' ')); 41 str.push('");'); 42 if (regResult[1].match(reg1)) { 43 str.push(regResult[1]); 44 } else { 45 str.push('arr.push('); 46 str.push(regResult[1]); 47 str.push(');'); 48 } 49 } 50 index1 = regResult.index + regResult[0].length; 51 } 52 if (index1 < tpl.length) { 53 if (isTrim) { 54 str += 'arr.push("' + tpl.slice(index1).replace(/\s+/g, ' ') + '");'; 55 } else { 56 str.push('arr.push("'); 57 str.push(tpl.slice(index1).replace(/\s+/g, ' ')); 58 str.push('");'); 59 } 60 } 61 isTrim ? str += 'return arr.join("");' : str.push('return arr.join("");'); 62 isTrim ? '' : str = str.join(''); 63 return new Function(str).call(data); 64 } 65 window.template = template; 66 }();
代码的逻辑很简单,通过isTrim判断是否采用字符串拼接的方法(字符串拼接在IE8一下浏览器中速度很是感人,所以一般采用数组的push方法);之后对获取到的tpl内容进行相关处理,处理成可供js执行的字符串,简单地例子如下:
<ul> <%for (var i = 0;i < data.arr.length;i++) {%> <li> <%for (var j = 0;j < data.arr[i].length;j++) {%> <a href="javascript:void(0)" data_id="<%data.arr[i][j].id%>"> <%data.arr[i][j].name%> <span>——</span> <%data.arr[i][j].value%> </a><br> <%}%> </li> <%}%> </ul>
上述代码会被解析成如下字符串代码(在高版本浏览器下):
str = '<ul>'; for (var i = 0;i < data.arr.length;i++) { str += '<li>'; for (var j = 0;j < data.arr[i][j].length;j++) { str += '<a href="javascript:void(0)"data_id="' + data.arr[i][j].id + '">' + data.arr[i][j].name + '<span>————</span>' + data.arr[i][j].value + '</a><br>'; } str += '</li>'; } str += '</ul>';
最终的字符串放在new Function()内执行(不是很了解这个方法的可以查看w3c,上面有详细的解释),生成需要的字符串。
(reg正则判断还是有问题,刚开始用的是 /<%([^%>]*)%>/g,但是这种匹配规则会不识别类似于<% if (a > b) {%>这种写法,但是有没有找到可以匹配<%开头、%>结尾、中间内容不存在%>的正则匹配规则,所以最终使用了 [^\n]代替,这样就必须每两个<%%>之间要有换行符隔开,但为了照顾到if判断中可能出现的>、<、%,也只好如此了。。。或许可以结合indexOf实现吧。。。)
一个简单地小demo如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="ie=edge,chrome=1"> <title>template</title> <script> /* * 一个简单地template模板语法写法 */ !function () { "use strict"; /* *tpl为传入的模板内容,data为数据 */ function template(tpl, data) { /*如果传入的是元素,则获取他的innerHTML赋值给他本身*/ if (tpl.tagName) { tpl = tpl.innerHTML; } /*将tpl中的"转义*/ tpl = tpl.replace(/"/g, '\\"'); /*判断使用字符串拼接方式还是使用push方式*/ var isTrim = ''.trim; /*定义正则表达式 reg: 匹配分隔符<%%> reg1: 匹配原生js代码 */ var reg = /<%([^\n]*)%>/g; var reg1 = /(for|while|do|if|else|switch|case|default|break|continue|{|})/g; /*定义一个空数组的字符串*/ var str = isTrim ? "var arr = [];" : ["var arr = [];"]; /*定义一个初始的下标,下方会需要*/ var index1 = 0; /*定义一个接收正则结果的变量*/ var regResult; while (regResult = reg.exec(tpl)) { if (isTrim) { str += 'arr.push("' + tpl.slice(index1, regResult.index).replace(/\s+/g, ' ') + '");'; if (regResult[1].match(reg1)) { str += regResult[1]; } else { str += 'arr.push(' + regResult[1] + ');'; } } else { str.push('arr.push("'); str.push(tpl.slice(index1, regResult.index).replace(/\s+/g, ' ')); str.push('");'); if (regResult[1].match(reg1)) { str.push(regResult[1]); } else { str.push('arr.push('); str.push(regResult[1]); str.push(');'); } } index1 = regResult.index + regResult[0].length; } if (index1 < tpl.length) { if (isTrim) { str += 'arr.push("' + tpl.slice(index1).replace(/\s+/g, ' ') + '");'; } else { str.push('arr.push("'); str.push(tpl.slice(index1).replace(/\s+/g, ' ')); str.push('");'); } } isTrim ? str += 'return arr.join("");' : str.push('return arr.join("");'); isTrim ? '' : str = str.join(''); return new Function(str).call(data); } window.template = template; }(); </script> </head> <body> <div id="app"></div> <script type="text/html" id="template"> <h1> <%data.text%> </h1> <div> <%if (!data.arr) {%> <h3>数据暂时为空哦!</h3> <div> <%}else{%> <ul> <%for (var i = 0;i < data.arr.length;i++) {%> <li> <%for (var j = 0;j < data.arr[i].length;j++) {%> <a href="javascript:void(0)" data_id="<%data.arr[i][j].id%>"> <%data.arr[i][j].name%> <span>——</span> <%data.arr[i][j].value%> </a><br> <%}%> </li> <%}%> </ul> <%}%> </div> </script> <script> var data = { text: "简单地template", arr: [ [{ id: 1000, name: "葛小伦", value: "银河之力" }, { id: 1001, name: "刘闯", value: "长城一号" }, { id: 1002, name: "蕾娜", value: "太阳之光" }, { id: 1003, name: "赵信", value: "人生赢家啊" }], [{ id: 2000, name: "宋江", value: "及时雨" }, { id: 2001, name: "晁盖", value: "天王" }, { id: 2002, name: "林冲", value: "豹子头" }, { id: 2003, name: "武松", value: "行者" }], [{ id: 3000, name: "曹操", value: "丞相" }, { id: 3001, name: "关羽", value: "汉寿亭侯" }] ] }; var html = template(document.getElementById('template').innerHTML, data); document.getElementById('app').innerHTML = html; </script> </body> </html>