本文主要介绍了只有20行的JavaScript模板引擎,并结合实例分析了JavaScript模板引擎的实现方法及相关注意事项。有需要的可以参考一下。
目录
前言1。识别片段2的提取。数据填充和逻辑处理。代码优化后记(译者注)。这个例子讲述了JavaScript模板引擎。分享给你,供你参考,如下:
原文链接:仅20行的JavaScript模板引擎
(译者吐槽:只收藏不喜欢是耍流氓)
前言
我还在开发我的JS预处理器AbsurdJS。它原本是一个CSS预处理器,后来扩展成了CSS/HTML预处理器,很快就会支持从JS到CSS/HTML的转换。就像模板引擎一样,它可以生成HTML代码,也就是说,它可以用数据填充模板中的标识片段。
所以希望能写一个能满足自己目前需求的模板引擎。AbsurdJS主要是作为NodeJS的一个模块,但是也可以在客户端使用。出于这个目的,我不能使用市面上现有的模板引擎,因为它们几乎都依赖于NodeJS,很难在浏览器中使用。我需要一个更小的纯JS编写的模板引擎。我浏览了John Resig写的这个博客,似乎这正是我所需要的。我稍微修改了一下代码,将其压缩到20行。
这段代码的运行原理很有意思。在这篇文章中,我将一步步向你展示约翰的绝妙想法。
1、提取标识片段
这是我们开始时将得到的结果:
var TemplateEngine=function(tpl,data) {
//魔法在这里.
}
var template='pHello,我的名字是%name%。我今年%岁了。/p ';
console.log(模板引擎(模板,{
姓名:“Krasimir”,
年龄:29岁
}));
一个简单的函数,将模板和数据作为参数传入。可以想象,我们希望得到以下结果:
费罗,我叫克拉西米。我今年29岁。/p
我们需要做的第一件事是获得%.%的标识片段,然后用来自传入引擎的数据填充它们。我决定使用正则表达式来完成这些功能。规律性不是我的强项,大家就将就一下吧。如果有更好的规律性,欢迎随时问我。
var re=/%([^%])?%/g;
我们将匹配所有以%开头并以%结尾的代码块,结尾的g(全局)表示我们将匹配多个代码块。有许多方法可以用来匹配规律性,但我们所需要的是一个可以加载字符串的数组,这正是exec所做的:
var re=/%([^%])?%/g;
var match=re . exec(TPL);
在控制台console.log(match)中,您可以看到:
[
%name% ',
姓名',
指数:21,
输入:
费罗,我的名字是%name%。我今年%岁了。/p '
]
我们得到了正确的匹配结果,但是正如您所看到的,只有一个徽标片段%name%匹配,因此我们需要一个while循环来获取所有的徽标片段。
var re=/%([^%])?%/g,匹配;
while(match=re.exec(tpl)) {
console . log(match);
}
运行,发现所有的识别碎片都被我们获取了。
2、数据填充与逻辑处理
获得标识片段后,我们需要用数据填充它们。使用。替换方法是最简单的方法:
var TemplateEngine=function(tpl,data) {
var re=/%([^%])?%/g,匹配;
while(match=re.exec(tpl)) {
tpl=tpl.replace(match[0],data[match[1]])
}
退货tpl
}
数据={
姓名:“克拉西米尔措涅夫”,
年龄:29岁
}
好的,正常操作。但显然这还不够。我们目前的数据结构非常简单,但在实际开发中,我们会面临更复杂的数据结构:
{
姓名:“克拉西米尔措涅夫”,
个人资料:{年龄:29 }
}
错误的原因是当我们在模板中输入%profile.age%时,我们的数据['profile.age']']未定义。很明显。替换方法不起作用。我们需要一些其他的方法来将真正的JS代码插入到%和%中,就像下面的例子:
var template='pHello,我的名字是%this.name%。我%this.profile.age%岁了。/p ';
这似乎不可能完成?约翰使用了新功能,即通过字符串创建功能的方法来完成这个功能。举个栗子:
var fn=新函数(' arg ',' console . log(arg 1);');
fn(2);//输出3
Fn是一个实函数,它包含一个参数,函数体是console.log(arg 1)。上述代码等效于以下代码:
var fn=function(arg) {
console . log(arg 1);
}
fn(2);//输出3
有了新函数,我们可以通过字符串创建一个函数,这正是我们所需要的。在创建这样一个函数之前,我们需要构造它的函数体。函数体应该返回一个最终的拼接模板。使用前面的模板字符串,想象这个函数应该返回的结果:
返回
费罗,我的名字是
这个名字。我是
this.profile.age
岁。/p ';
显然,我们把模板分成了文本和JS代码。就像上面的代码一样,我们使用简单的字符串串联来得到最终的结果,但是这种方法并不能100%满足我们的需求,因为这样我们就要处理循环之类的JS逻辑,就像这样:
var模板=
我的技能:'
% for(this . skills中的var索引){% '
a href=''%this.skills[index]%/a '
'%}%';
如果使用字符串连接,结果将如下所示:
返回
我的技能:'
for(this . skills中的var指数){
a href=' ' '
this . skills[索引]
/a '
}
当然,这将是一个错误。所以我决定参考约翰的文章写逻辑。——我将所有字符串放入一个数组中,并在最后将它们拼接起来:
var r=[];
r.push('我的技能:');
for(this . skills中的var指数){
r . push(' a href=' ' ');
r . push(this . skills[index]);
r . push('/a ');
}
返回r . join(“”);
下一个逻辑步骤是整理每一行代码以生成一个函数。我们从模板中提取了一些信息,知道了标识段的内容和位置,所以可以通过一个指针变量(cursor)帮助我们得到最终的结果:
var TemplateEngine=function(tpl,data) {
var re=/%([^%])?%/g,
code=' var r=[];\n ',
cursor=0,匹配;
var add=function(line) {
code=' r . push(' line . replace(/'/g,' \ ' ')' ');\ n ';
}
while(match=re.exec(tpl)) {
add(tpl.slice(cursor,match . index));
add(match[1]);
cursor=match.index match[0]。长度;
}
add(tpl.substr(游标,TPL . length-cursor));
code=' return r . join(');';//-返回结果
console.log(代码);
退货tpl
}
var template='pHello,我的名字是%this.name%。我%this.profile.age%岁了。/p ';
console.log(模板引擎(模板,{
姓名:“克拉西米尔措涅夫”,
个人资料:{年龄:29 }
}));
代码以数组声明开始,并作为整个函数的函数体。正如我所说的,指针变量cursor指示我们在模板中的位置,我们需要它遍历所有字符串并跳过充满数据的段。另外,add函数的任务是将字符串插入到代码变量中,作为构建函数体的处理方法。这里有一个棘手的问题。我们需要跳过标识符%%,否则JS脚本将无效。如果我们直接运行上面的代码,结果将如下:
var r=[];
r.push('pHello,我叫');
r . push(' this . name ');
r.push('。我是’);
r . push(' this . profile . age ');
返回r . join(“”);
哦.这不是我们想要的。不应引用This.name和this.profile.age。让我们改进添加功能:
var add=function(line,js) {
js?code=' r . push(' line ');\n ':
code=' r . push(' line . replace(/'/g,' \ ' ')' ');\ n ';
}
var匹配;
while(match=re.exec(tpl)) {
add(tpl.slice(cursor,match . index));
add(匹配[1],真);//-说这是有效的js
cursor=match.index match[0]。长度;
}
标识片段中的内容将由一个布尔值控制。现在我们有了一个正确的函数体:
var r=[];
r.push('pHello,我叫');
r . push(this . name);
r.push('。我是’);
r . push(this . profile . age);
返回r . join(“”);
我们需要做的下一件事是生成并运行这个函数。在这个模板引擎的最后,我们使用下面的代码,而不是直接返回一个tpl对象:
返回新函数(code.replace(/[\r\t\n]/g,' '))。应用(数据);
我们甚至不需要向函数传递任何参数,因为apply方法已经为我们完成了这一步。它会自动设置作用域,这也是this.name能够运行的原因,这就指向了我们的数据。
3、代码优化
差不多完成了。最后一点,我们需要支持更复杂的表达式,比如if/else表达式和循环。让我们尝试使用相同的示例运行以下代码:
var模板=
我的技能:'
% for(this . skills中的var索引){% '
a href=' # ' % this . skills[index]%/a '
'%}%';
console.log(模板引擎(模板,{
技能:['js ',' html ',' css']
}));
结果将被报告为错误,该错误为未捕获的语法错误:的意外标记。仔细观察,通过代码变量,我们可以找出问题:
var r=[];
r.push('我的技能:');
r . push(for(this . skills中的var index){);
r . push(' a href=' ' ');
r . push(this . skills[index]);
r . push('/a ');
r . push(});
r . push(“”);
返回r . join(“”);
包含for循环的代码不应该压入数组,而应该直接放在脚本中。为了解决这个问题,我们需要在将代码推送到代码变量之前再进行一步判断:
var re=/%([^%])?%/g,
reExp=/(^()?(if | for | else | switch | case | break | { | })(。*)?/g,
code=' var r=[];\n ',
光标=0;
var add=function(line,js) {
js?code=line.match(reExp)?line ' \ n ':' r . push(' line ');\n ':
code=' r . push(' line . replace(/'/g,' \ ' ')' ');\ n ';
}
我们增加了一条新规则。这种规律性的作用是,如果一段JS代码以if,for,else,switch,case,break,|,就直接加到函数体中;如果没有,它将被推入代码变量。下面是修改后的结果:
var r=[];
r.push('我的技能:');
for(this . skills中的var指数){
r . push(' a href=' # ' ');
r . push(this . skills[index]);
r . push('/a ');
}
r . push(“”);
返回r . join(“”);
当然,正确的执行:
我的技能:a href=' # ' js/aa href=' # ' html/aa href=' # ' CSS/a
下一个版本会给我们更强大的功能。我们可能会将更复杂的逻辑放入模板中,就像这样:
var模板=
我的技能:'
%if(this.showSkills) {% '
% for(this . skills中的var索引){% '
a href=' # ' % this . skills[index]%/a '
'%}%'
%} else {% '
p一个/p '
'%}%';
console.log(模板引擎(模板,{
技能:['js ',' html ',' css'],
表演技巧:真的
}));
经过一些细微的优化,最终版本如下:
var template engine=function(html,选项){
var re=/%([^%])?%/g,reExp=/(^()?(if | for | else | switch | case | break | { | })(。*)?/g,code=' var r=[];\n ',游标=0,匹配;
var add=function(line,js) {
js?(code=line.match(reExp)?line ' \ n ':' r . push(' line ');\n '):
(代码=行!='' ?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]。长度;
}
add(html.substr(cursor,html . length-cursor));
code=' return r . join(');';
返回新函数(code.replace(/[\r\t\n]/g,' '))。应用(选项);
}
优化后的代码甚至不到15行。
后记(译者注)
这是我第一次完整的翻译一篇文章。请原谅我句子中的许多错误和遗漏。以后我会继续努力翻译和分享更多的优秀文章。
因为对前端框架、模板引擎之类的工具特别感兴趣,而且很想学习其中的原理,所以找了一个比较简单的模板引擎进行研究。谷歌看到这篇文章后,觉得很优秀。一步一步的讲解生动而深入,代码也是我自己测试的,正确的得到了文章中描述的结果。
模板引擎的设计思路有很多,本文只是其中之一。它的性能和其他参数都需要测试和改进,仅供学习。
谢谢大家~
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun来测试上述代码的运行效果。
对更多JavaScript相关内容感兴趣的读者可以查看我们的专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》和《JavaScript数学运算用法总结》。
希望这篇文章对大家的JavaScript编程有所帮助。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。