实现模板引擎

作者: web_Tracy | 来源:发表于2018-11-05 11:27 被阅读5次

概念


模板引擎(这里指的时用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档

模板引擎不属于特定技术领域,它是跨领域平台的概念。在Asp下有模板引擎,在PHP也有模板引擎,在#C下也有,甚至JavaScriptWinForm开发会用到的模板引擎技术。

举例:在一个ul里插入lili包含nameage,最后生成结构如下

<ul>
  <li>XXX - 20</li>
</ul>

实现方式:
1.拼接HTML 字符串

var html = '<li>' + name + ' - ' + age + '</li>';
var ulEl = document.getElementsByTagName('ul')[0];
ulEl.innerHTML = html;

2.构造DOM

var ulEl = document.getElementsByTagName('ul')[0];
var liEl = document.createElement('li');
liEl.textContent= name + ' - ' + age;
ulEl.appendChild(liEl);

但这种方式写起来比较麻烦,下面就在方式1的基础上进行优化

字符串格式化


如果想要字符串不包含变量,跟变量区分开,而是一个完整的字符串,不含加号,方便书写。这时就需要用特定的格式代替变量,格式化前后变化如下:

// before
var li = '<li>' + obj[i].name + '-' + obj[i].age + '</li>';
// after
var li = stringFormat('<li>{0}-{1}</li>', obj[i].name, obj[i].age);

将字符串格式化需封装stringFormat,变量用{}括起来表示,传入参数的按顺序放入{0}{1}上,由函数返回字符串
声明函数时,如果需传入多个参数就需要写多个形参,然而一个函数最多可以有255个参数,参数太多书写时影响美观,直接传入需要处理的字符串即可

function stringFormat(str){
  // arguments 是传入参数的类数组,不可直接使用数组的方法,不能直接写 arguments.slice(1)
  var params = [].slice.call(arguments, 1);
  // 用正则表达式将 {} 替换成对应变量的值
  var reg = /\{(\d+)\}/g;
  str= str.replace(reg, function(){
    console.log(arguments);
    var index = arguments[1];
    return params[index];
  });
  
  return str;
}

console.log(stringFormat('{0} - {1}', 'xxx', 20));

运行结果如下


字符串格式化之后效率提升不少,但是还有个问题,如果传入的参数没按顺序,完全打乱,那出来的结果也不是预期中的,还需要进一步改进,接下来就要说到本文的重点了——模板引擎

模板引擎


模板引擎其实就是字符串格式化的升级版
在上述的例子基础上进行改进,可以将数据存到一个对象里,可将{0}{1}分别替换成{name}{age},也就是对象里的属性。代码如下:

function stringFormat(str, data){
  var params = [].slice.call(arguments, 1);
  var reg = /\{(\S+)\}/g;
  str= str.replace(reg, function(){
    console.log(arguments);
    var index = arguments[1];
    return data[index];
  });
  
  return str;
}

var data = {
    name: "Krasimir",
    age: 29
};

console.log(stringFormat('{name} - {age}', data));

运行结果如下:


还有另一种方法,为跟上一个例子做点区分,将模板换成<%property%>,于是可将{name}{age}分别替换成<%name%><%age%>,用到了reg.exec遍历查找传入字符串,匹配模板,然后用str.replace替换成对应的属性值

先了解exec的作用,代码和运行结果如下:


从结果可看出:
  • reg不是全局匹配,则每次运行reg.exec(str)得到的结果都是字符串中第一个匹配的数组
  • reg是全局匹配,则每次运行reg.exec(str)得到的结果都按顺序会往下匹配,当全部匹配完之后,再执行一次reg.exec(str)则返回null

利用reg.exec匹配之后再替换,代码如下:

var TemplateEngine = function(tpl, data){
  var reg = /<%([^%>]+)?%>/g;
  var match;
  while(match = reg.exec(tpl)){
    console.log(match);
    tpl = tpl.replace(match[0], data[match[1]]);
  }
  return tpl;
};

var template = '<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>';

var data = {
    name: "Krasimir",
    age: 29
};

var string = TemplateEngine(template, data);

console.log(string);

运行结果如下:


一个简易版的模板引擎就实现了,但如果数据是这样的,如下所示:

var data = {
    name: "Krasimir",
    info: {
      age: 29
    }
};

要取出age的值,传入的模板字符串改成var template = '<p>Hello, my name is <%name%>. I\'m <%info.age%> years old.</p>'TemplateEngine里匹配到并返回的就是data["info.age"],这样得不到正确的age属性值
再者,如果想插入多行字符串,上面这方法就得执行多次;如果想在字符串中插入JavaScript代码,循环插入多行字符串,实现如下代码:

var template = 
'My skills:' + 
'<%if(this.showSkills) {%>' +
    '<%for(var index in this.skills) {%>' + 
    '<a href="#"><%this.skills[index]%></a>' +
    '<%}%>' +
'<%} else {%>' +
    '<p>none</p>' +
'<%}%>';

var string = TemplateEngine(template, {
    skills: ["js", "html", "css"],
    showSkills: true
})

document.body.innerHTML = string

也就是说TemplateEngine函数输出的结果得是:

var line = "";
line += "My skills:";
if(this.showSkills) {
  for(var index in this.skills) {
    line += "<a href=\"#\">";
    line += this.skills[index];
    line += "</a>";
  }
} else {
  line += "<p>none</p>";
}
return line;

返回的结果得当作JavaScript代码来执行
怎么把字符串当作代码来执行呢?Function 构造函数了解一下

new Functon (arg1, arg2, ... argN, functionBody)
每个arg都是一个参数,最后一个参数是函数主体(要执行的代码)。这些参数必须是字符串
举例:

function sayHi(sName, sMessage) {
  alert("Hello " + sName + sMessage);
}

还可以定义成:

var sayHi = new Function("sName", "sMessage", "alert(\"Hello \" + sName + sMessage);");

函数主体字符串若涉及到双引号则需进行转义(\"

有了可以将字符串当成函数主体执行的函数构造器,于是TemplateEngine的封装如下:

var TemplateEngine = function(tpl, data){
  var reg = /<%([^%>]+)?%>/g;
  var regJS = /^( )?(^if|else|switch|case|default|break|for|{|})(.*)/g;
  var cursor = 0;
  var code = 'var line = "";\n';
  var match;
  
  var isExp = function(str){
    if(str.match(regJS)){
      return str;
    }else {
      return 'line += ' + str + ';';
    }
  };
  
  while(match = reg.exec(tpl)){
    if(cursor !== match.index){
      code += 'line += "' + tpl.slice(cursor, match.index).replace(/"/g, '\\"') + '";\n';
    }    
    code += isExp(match[1])+'\n';
    cursor = match.index + match[0].length;
  }
  code += tpl.substr(cursor, tpl.length - 1);
  code += 'return line;';
  console.log(code);
  var str = new Function(code.replace(/[\r\t\n]/g, '')).apply(data);
  return str;
};

var template = 
'My skills:' + 
'<%if(this.showSkills) {%>' +
    '<%for(var index in this.skills) {%>' + 
    '<a href="#"><%this.skills[index]%></a>' +
    '<%}%>' +
'<%} else {%>' +
    '<p>none</p>' +
'<%}%>';

var string = TemplateEngine(template, {
    skills: ["js", "html", "css"],
    showSkills: true
});

document.body.innerHTML = string;

控制台打印如下:


  • showSkills: true
    文档中插入html效果:

    showSkills: true
    再打开调式看下生成的html,如下所示:
    showSkills: true
  • showSkills: false
    为了验证准确性,再将showSkills改为false
    文档中插入html效果:

    showSkills: false
    再打开调式看下生成的html,如下所示:
    showSkills: false

更具体的思路可参考只有20行Javascript代码!手把手教你写一个页面模板引擎

原理


介绍了以上几种实现模板引擎的方法之后,可知其实现原理如下:


将模板和数据输入到模板引擎,执行该函数,输出结果即HTML代码段,再将该片段插入到文档中
模板引擎可用于任意一端,前后端即插即用,不局限于生成内容的语法,只要生成内容为字符串文本即可。然而,此模板引擎依赖于innerHTML,存在脚本注入的风险;且对于数据的更改,需要重新渲染模板,所以在初次渲染和之后的模板更新需要耗费同样的资源

参考


相关文章

  • Enjoy模板引擎原理

    模板引擎是web开发中必不可少的部分,Enjoy模板引擎做为JFinal的默认模板引擎,也可以单独使用。它的实现非...

  • 使用Poi-tl实现的文档导出

    World模板引擎[http://deepoove.com/poi-tl/] 项目利用这个模板引擎实现了超级复杂的...

  • 实现模板引擎

    概念 模板引擎(这里指的时用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定...

  • 实现模板引擎

    字符串替换 Template(模板) 目录 字符串拼接 string format(字符串格式化) 模板替换 自制...

  • 实现模板引擎

    模板引擎是wap开发的一大利器,方便我们生成复杂的动态页面。我们学习一下如何用Python实现一个模板引擎 一、目...

  • js模板引擎的实现

    这里实现一个较传统的模板引擎。模板引擎的实现原理其实就是拼接字符串,+号拼接速度最快。这里我们采用这种方式实现一个...

  • 如何了解JavaScript模板引擎实现原理

    这篇文章主要介绍了JavaScript模板引擎实现原理,结合实例形式详细分析了JavaScript模板引擎原理、定...

  • laravel 5 blade

    参考Blade 模板引擎。Blade是一个模板引擎(什么叫模板引擎,参考浅谈模板引擎),文件需要采用blade.p...

  • SpringBoot系列之集成jsp模板引擎

    SpringBoot系列之集成jsp模板引擎@[toc] 1、模板引擎简介 引用百度百科的模板引擎解释: 模板引擎...

  • 每日复盘2018.10.10

    上午: 1. 模板引擎的选择,实现模板正确渲染 下午: 1. 使用umi-plugin-react...

网友评论

    本文标题:实现模板引擎

    本文链接:https://www.haomeiwen.com/subject/tlbdtqtx.html