时间:2023-06-07 08:21:01 | 来源:网站运营
时间:2023-06-07 08:21:01 来源:网站运营
模板引擎的整理归纳:var songs =[ {name:'刚刚好', singer:'薛之谦', url:'http://music.163.com/xxx'}, {name:'最佳歌手', singer:'许嵩', url:'http://music.163.com/xxx'}, {name:'初学者', singer:'薛之谦', url:'http://music.163.com/xxx'}, {name:'绅士', singer:'薛之谦', url:'http://music.163.com/xxx'}, {name:'我们', singer:'陈伟霆', url:'http://music.163.com/xxx'}, {name:'画风', singer:'后弦', url:'http://music.163.com/xxx'}, {name:'We Are One', singer:'郁可唯', url:'http://music.163.com/xxx'} ] //拼接字符串,有一定恶意脚本注入风险 遍历 var html = ''; html +='<div class="song-list">' html +=' <h1>热歌榜</h1>' html +=' <ol>' for(var i=0;i<songs.length;i++){ html += '<li>'+songs[i].name+' - '+songs[i].singer+'</li>' } html +=' </ol>' html +='</div>' document.body.innerHTML =html;
// 构造DOM对象 遍历 缺点复杂; var elDiv = document.createElement('div') elDiv.className = 'song-list'; var elH1 =document.createElement('h1') elH1.appendChild(document.createTextNode('热歌榜')) var elList = document.createElement('ol') for(var i = 0; i<songs.length;i++){ var li = document.createElement('li') li.textContent = songs[i].name +' - ' + songs[i].singer elList.appendChild(li) } elDiv.appendChild(elH1); elDiv.appendChild(elList); document.body.appendChild(elDiv);
可以看到上述两种方式虽然可以达成需求,但是尤其繁琐且缺乏规范,很容易出错。var template = '<p>Hello,my name is <%name%>. I am <%age%> years old.</p>'; var data ={ name:'zyn', age:31 } var TemplateEngine = function (tpl,data){ var regex = /<%([^%>]+)?%>/g; while(match = regex.exec(tpl)){ tpl = tpl.replace(match[0],data[match[1]]) } return tpl } var string = TemplateEngine(template,data) console.log(string);
这里其实就是把模板中需要替换的字符串做了个标记,这里是以<%...%>作为标记,然后替换时基于正则捕捉该标记并进行数据源的替换(通过同一个key进行)模板文件: var template = '<p>Hello,my name is <%name%>. I am <%age%> years old.</p>';数据: var data ={ name:'zyn', age:31 }模板引擎: var TemplateEngine = function (tpl,data){ var regex = /<%([^%>]+)?%>/g; while(match = regex.exec(tpl)){ tpl = tpl.replace(match[0],data[match[1]]) } return tpl }HTML文件: var string=TemplateEngine(template,data) document.body.innerHTML= string
var data ={ name:'zyn', profile:{age:31} }
在模板中使用<%profile.age%>的话,代码会被替换成data[‘profile.age’],结果是undefined,因为括号型没办法认识.符号,当然我们可以改进Template函数来分解复杂对象转换为[][]的形式。但是这里我们换一个方式。var template = '<p>Hello, my name is <%this.name%>. I/'m <%this.profile.age%> years old.</p>'
这里为了之后的示范,我们补充一下关于new Function的知识,这个函数的构造函数可以根据传入参数来动态生成一个函数,包括函数入参,函数体等:var fn = new Function("num", "console.log(num + 1);");fn(2); //3
等同于:var fn = function(num) { console.log(num + 1);}fn(2); // 3
这里我们思路基本明确了,就是希望构建一个函数体字符串,然后利用JS代码执行过程帮我们把数据绑定到模板上面。var Arr=[];Arr.push("<p>Hello,my name is");Arr.push(this.name);Arr.push("i am");Arr.push(this.proflie.age)Arr.push("years old</p>")return Arr.join('')
接下来需要做的还是去寻找模板中的标记位,即<%...%>片段,然后遍历所有的匹配项将其push到字符串数组中去,最后借助new Function完成。var TemplateEngine = function(tpl, data) { // 正则全局匹配 // code用于保存函数体字符串 // cursor是游标,用于记录tpl处理的位置 var re = /<%([^%>]+)?%>/g, code = 'var Arr=[];/n', cursor = 0; // 函数add负责将解析的代码行添加到code函数体中 // 后面的replace是将code包含的双引号进行转义 var add = function(line) { code += 'Arr.push("' + line.replace(/"/g, '//"') + '");/n'; } // 循环处理模板,每当存在匹配项就进入循环体 while(match = re.exec(tpl)) { add(tpl.slice(cursor, match.index)); add(match[1]); cursor = match.index + match[0].length; } add(tpl.substr(cursor, tpl.length - cursor)); code += 'return Arr.join("");'; // <-- return the result console.log(code); return tpl;}var template = '<p>Hello, my name is <%this.name%>. I/'m <%this.profile.age%> years old.</p>';var data = { name: "zyn", profile: { age: 29 }}console.log(TemplateEngine(template, data));
循环过程:第一次循环:match=[ 0:<%this.name%>", 1:"this.name", index:21, input:"<p>Hello, my name is<%this.name%>.I'm<%this.profile.age%>years old.</p>", length:2 ]tpl.slice(cursor, match.index) = "<p>Hello, my name is "执行函数add("<p>Hello, my name is ")code="var Arr=[];Arr.push("<p>Hello, my name is ");"在执行add(match[1]);match[1]="this.name"code ="var Arr=[];Arr.push("<p>Hello, my name is ");Arr.push("this.name");" cursor = match.index + match[0].length;cursor = 21+13=34;//就是<%this.name%>最后一位的位置;第二次循环跟第一次一样继续把模板文件添加到code上;两次循环完成后code = "var Arr[];Arr.push("<p>Hello, my name is ");Arr.push("this.name");Arr.push(". I'm ");Arr.push("this.profile.age")"cursor =60 ;然后执行: add(tpl.substr(cursor, tpl.length - cursor));cursor =60 ; tpl.length=75 tpl.substr(cursor, tpl.length - cursor)截取最后一段模板文件 years old.</p>code += 'return Arr.join("");'code = "var Arr[];Arr.push("<p>Hello, my name is ");Arr.push("this.name");Arr.push(". I'm ");Arr.push("this.profile.age")Arr.push("years old </p>")return Arr.join("")"如果还不明白可以复制代码在代码上打几个断点看下执行的过程,很快就能明白;
最后我们会在控制台里面看见如下的内容:var Arr[];Arr.push("<p>Hello, my name is ");Arr.push("this.name");Arr.push(". I'm ");Arr.push("this.profile.age")Arr.push("years old </p>")return Arr.join("")<p>Hello, my name is <%this.name%>. I'm <%this.profile.age%> years old.</p>
这里还存在一些问题:var TemplateEngine = function(html, options) { var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var Arr=[];/n', cursor = 0; var add = function(line, js) { js? (code += line.match(reExp) ? line + '/n' : 'r.push(' + line + ');/n') : (code += line != '' ? 'r.push("' + line.replace(/"/g, '//"') + '");/n' : ''); return add; } while(match = re.exec(html)) { add(html.slice(cursor, match.index))(match[1], true); cursor = match.index + match[0].length; } add(html.substr(cursor, html.length - cursor)); code += 'return r.join("");'; return new Function(code.replace(/[/r/t/n]/g, ''));}
这里感兴趣的可以基于上面的示例自己尝试去实现一下上一小节的例子~关键词:归纳,整理,引擎,模板