首页 » 网站建设 » php中tpl技巧_JavaScript模板引擎事理与用法

php中tpl技巧_JavaScript模板引擎事理与用法

访客 2024-12-11 0

扫一扫用手机浏览

文章目录 [+]

一、序言

什么是模板引擎,说的大略点,便是一个字符串中有几个变量待定。
比如:

php中tpl技巧_JavaScript模板引擎事理与用法

var tpl = 'Hei, my name is <%name%>, and I\'m <%age%> years old.';

通过模板引擎函数把数据塞进去,

php中tpl技巧_JavaScript模板引擎事理与用法
(图片来自网络侵删)

var data = { \公众name\公众: \公众Barret Lee\"大众, \公众age\公众: \"大众20\"大众};var result = tplEngine(tpl, data);//Hei, my name is Barret Lee, and I'm 20 years old.

那这玩意儿有什么浸染呢?实在他便是一个预处理器(preprocessor),搞php开拓的童鞋对Smarty一定是十分熟习,Smarty是一个php模板引擎,tpl中待处理的字符通过数据匹配然后输出相应的html代码,加之比较给力的缓存技能,其速率和易用性是非常给力的!
JS Template也是一样的,我们的数据库里保存着数以千万计的数据,而每一条数据都是通过同一种办法输入,就拿上面的例子来说,我们不可能在数据库里存几千条\"大众Hei, my name...\"大众,而是只保存对应的name和age,通过模板输出结果。

JS模板引擎该当做哪些事情?看看下面一串代码:

var tpl = '<% for(var i = 0; i < this.posts.length; i++) {' +  'var post = posts[i]; %>' + '<% if(!post.expert){ %>' + '<span>post is null</span>' + '<% } else { %>' + '<a href=\公众#\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\公众 rel=\"大众external nofollow\"大众 ><% post.expert %> at <% post.time %></a>' + '<% } %>' +

一个基本的模板引擎至少可以担保上面的代码可以正常解析。
如送入的数据是:

var data = { \"大众posts\公众: [{ \"大众expert\"大众: \"大众content 1\"大众, \"大众time\公众: \"大众yesterday\"大众 },{ \"大众expert\"大众: \"大众content 2\公众, \"大众time\"大众: \"大众today\公众 },{ \公众expert\"大众: \公众content 3\公众, \公众time\公众: \"大众tomorrow\"大众 },{ \"大众expert\"大众: \公众\"大众, \"大众time\"大众: \"大众eee\"大众 }]};

可以输出

<a href=\公众#\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\公众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 >content 1 at yesterday</a><a href=\公众#\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\公众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\公众 >content 2 at today</a><a href=\"大众#\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\"大众 >content 3 at tomorrow</a><span>post is null</span>

下面就详细说说这个模板引擎的事理是啥样的。

二、JS模板引擎的实现事理

1.正则抠出要匹配的内容 针对这一串代码,通过正则获取内容

var tpl = 'Hei, my name is <%name%>, and I\'m <%age%> years old.';var data = { \"大众name\公众: \公众Barret Lee\"大众, \公众age\"大众: \"大众20\"大众};

最大略的办法便是通过replace函数了:

var result = tpl.replace(/<%([^%>]+)?%>/g, function(s0, s1){ return data[s1];});

通过正则更换,我们很轻松的拿到了result,你可以去试一试,他正是我们想要的结果。
但是这里又有了一个问题,改一下data和tpl,

var tpl = 'Hei, my name is <%name%>, and I\'m <%info.age%> years old.';var data = { \"大众name\"大众: \"大众Barret Lee\公众, \公众info\公众: { age\公众: \公众20\"大众}};

再用上面的办法去获取结果,呵呵,弗成了吧~ 这里data[\公众info.age\公众]本身便是undefined,以是我们须要换一种办法来处理这个问题,那便是将它转换成真正的JS代码。
如:

return 'Hei, my name is ' + data.name + ', and I\'m ' + data.info.age' + ' years old.'

但是接着又有一个问题来了,当我们的代码中涌现for循环和if的时候,上面的转换明显是不起浸染的,如:

var tpl = 'Posts: ' + '<% for(var i = 0; i < post.length; i++) {'+ '<a href=\"大众#\"大众

如果连续采取上面的办法,得到的结果便是:

return 'Posts: ' + for(var i = 0; i < post.length; i++) { + '<a href=\"大众#\公众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\"大众 >' + post[i].exper + '</a>' + }

这显然不是我们乐意看到的,轻微不雅观察一下上面的构造,如果可以返回一个这样的结果也挺不错哦:

'Posts: 'for(var i = 0; i < post.length; i++) { '<a href=\"大众#\"大众 rel=\公众external nofollow\公众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 >' + post[i].exper + '</a>'}

但是我们须要得到的是一个字符串,而不是上面这样零散的片段,因此可以把这些东西装入数组中。

2.装入数组

var r = [];r.push('Posts: ' );r.push(for(var i = 0; i < post.length; i++) {);r.push('<a href=\"大众#\"大众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\公众 >');r.push(post[i].exper);r.push('</a>');r.push(});

有人看到上面的代码就要笑了,第三行和末了一行代码的逻辑明显是禁绝确的嘛,那肿么办呢?呵呵,很大略,不放进去就行了呗,

var r = [];r.push('Posts: ' );for(var i = 0; i < post.length; i++) { r.push('<a href=\"大众#\"大众 rel=\公众external nofollow\公众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\"大众external nofollow\"大众 >'); r.push(post[i].exper); r.p

这样的逻辑就十分完善了,不存在太多的漏洞,但是这个转化的过程是如何实现的?我们必须还是要写一个解析的模板函数出来。
3.分辨js逻辑部分

var r = [];tpl.replace(/<%([^%>]+)?%>/g, function(s0, s1){ //塌台了,这里貌似又要回到上面那可笑的逻辑有缺点的一步啦... 该怎么处理比较好?});

塌台了,这里貌似又要回到上面那可笑的逻辑有缺点的一步啦... 该怎么处理比较好?我们知道,JS给我们供应告终构函数的“类”,

var fn = new Function(\"大众data\"大众, \"大众var r = []; for(var i in data){ r.push(data[i]); } return r.join(' ')\"大众);fn({\公众name\公众: \"大众barretlee\"大众, \"大众age\公众: \"大众20\"大众}); // barretlee 20

道了这个就好办了,我们可以把逻辑部分和非逻辑部分的代码链接成一个字符串,然后利用类似fn的函数直接编译代码。
而/<%([^%>]+)?%>/g,这一个正则只能把逻辑部分匹配出来,要想把所有的代码都组合到一起,必须还得匹配非逻辑部分代码。
replace函数虽然很强大,他也可以完成这个任务,但是实现的逻辑比较晦涩,以是我们换其余一种办法

var reg = /<%([^%>]+)?%>/g;var tpl = 'Hei, my name is <%name%>, and I\'m <%age%> years old.';var match = reg.exec(tpl);console.log(match);

看到的是:

[ 0: \公众<%name%>\"大众, 1: name, index: 16, input: \公众Hei, my name is <%name%>, and I'm <%age%> years old.\"大众 length: 2]

这。


我们可是想得到所有的匹配啊,他竟然只获取了name而忽略了后面的age,好吧,对正则轻微熟习点的童鞋一定会知道该当这样处理:

var reg = /<%([^%>]+)?%>/g;while(match = reg.exec(tpl)) { console.log(match);}

关于正则表达式的内容就不在这里细说了,有兴趣的同学可以多去理解下match,exec,search等正则的干系函数。
这里紧张是靠match的index属性来定位遍历位置,然后利用while循环获取所有的内容。

4.引擎函数

以是我们的引擎函数雏形差不多就出来了:

var tplEngine = function(tpl, data){ var reg = /<%([^%>]+)?%>/g, code = 'var r=[];\n', cursor = 0; //紧张的浸染是定位代码末了一截 var add = function(line) { code += 'r.push(\公众' + line.replace(/\公众/g, '\\\"大众') + '\"大众);\n'; }; while(match = reg.exec(tpl)) { add(tpl.slice(cursor, match.index)); //添加非逻辑部分 add(match[1]); //添加逻辑部分 match[0] = \"大众<%\"大众 + match[1] + \公众%>\"大众; cursor = match.index + match[0].length; } add(tpl.substr(cursor, tpl.length - cursor)); //代码的末了一截 如:\公众 years old.\公众 code += 'return r.join(\公众\"大众);'; // 返回结果,在这里我们就拿到了装入数组后的代码 console.log(code); return tpl;};

这样一来,测试一个小demo:

var tpl = '<% for(var i = 0; i < this.posts.length; i++) {' +  'var post = posts[i]; %>' + '<% if(!post.expert){ %>' + '<span>post is null</span>' + '<% } else { %>' + '<a href=\"大众#\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\"大众external nofollow\公众 rel=\公众external nofollow\"大众 rel=\"大众external nofollow\"大众 rel=\公众external nofollow\"大众 rel=\公众external nofollow\"大众 ><% post.expert %> at <% post.time %></a>' + '<% } %>' + '<% } %>';tplEngine(tpl, data);

返回的结果让人很满意:

var r=[];r.push(\"大众\"大众);r.push(\"大众 for(var i = 0; i < this.posts.length; i++) {var post = posts[i]; \"大众);r.push(\"大众\"大众);r.push(\公众 if(!post.expert){ \"大众);r.push(\公众<span>post is null</span>\"大众);r.push(\"大众 } else { \"大众);r.push(\公众<a href=\\"大众#\\"大众>\公众);r.push(\公众 post.expert \"大众);r.push(\公众 at \"大众);r.push(\"大众 post.time \公众);r.push(\"大众</a>\公众);r.push(\"大众 } \"大众);r.push(\公众\"大众);r.push(\"大众 } \"大众);r.push(\公众\"大众);

不过我们并须要for,if,switch等这些东西也push到r数组中去,以是呢,还得改进下上面的代码,如果在line中创造了包含js逻辑的代码,我们就不应该让他进门:

regOut = /(^( )?(if|for|else|switch|case|break|{|}))(.)?/g;var add = function(line, js) { js? code += line.match(regOut) ? line + '\n' : 'r.push(' + line + ');\n' : c

以是我们只剩下末了一步事情了,把data扔进去!

5.把data扔进去

没有比完成这东西更大略的事情啦,通过上面对Function这个函数的讲解,大家该当也知道怎么做了。

return new Function(code).apply(data);

利用apply的浸染便是让code中的一些变量浸染域绑定到data上,不然浸染域就会跑到global上,这样得到的数据索引就会出问题啦~ 当然我们可以再优化一下:

return new Function(code.replace(/[\r\t\n]/g, '')).apply(data);

把回车换行以及tab键都给匹配掉,让代码更加干净一点。
那么终极的代码便是:

var tplEngine = function(tpl, data) { var reg = /<%([^%>]+)?%>/g, regOut = /(^( )?(if|for|else|switch|case|break|{|}))(.)?/g, code = 'var r=[];\n', cursor = 0; var add = function(line, js) { js? (code += line.match(regOut) ? line + '\n' : 'r.push(' + line + ');\n') : (code += line != '' ? 'r.push(\"大众' + line.replace(/\公众/g, '\\\公众') + '\"大众);\n' : ''); return add; } add(tpl.slice(cursor, match.index))(match[1], true); cursor = match.index + match[0].length; } add(tpl.substr(cursor, tpl.length - cursor)); code += 'return r.join(\"大众\"大众);'; return new Function(code.replace(/[\r\t\n]/g, '')).apply(data);};

三、运用处景

毕竟是前端代码,以是写出来是要为前端做事的,平时我们处理的一样平常是一个html的模板,常日的情形下,模板代码是放在script标签或者textarea中,以是首先是要获取到这里头的东西,然后再来做解析。

var barretTpl = function(str, data) { //获取元素 var element = document.getElementById(str); if (element) { //textarea或input则取value,其它情形取innerHTML var html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML; return tplEngine(html, data); } else { //是模板字符串,则天生一个函数 //如果直接传入字符串作为模板,则可能变革过多,因此不考虑缓存 return tplEngine(str, data); } var tplEngine = function(tpl, data) { // content above };};

这样一来就更加大略了,利用办法便是 barretTpl(str, data), 这里的str可以是模板代码,也可以是一个DOM元素的id~

四、优化以及功能拓展

统共就三四十行代码,完成的东西肯定是一个简洁版的,不过对付一个大略的页面而言,这几行代码已经足够利用了,如果还想对他做优化,可以从这几个方面考虑:

优化获取的模板代码,比如去掉行尾空格等符号转义,如果我们想输出<span>hehe</span>类似这样的源代码,在push之前必须进行转义 代码缓存,如果一个模板会常常利用,可以将它用一个数组缓存在barretTpl闭包内用户自己设置分隔符

结语

感谢您的不雅观看,如有不敷之处,欢迎批评示正。

标签:

相关文章