自制一个简单的模板引(han)擎(shu)

原理

说大了 实际上是模板函数 原理呢就是简单的字符串替换

第一版

var data = {
username: 'Muhha'
}
str = '<%=username%>';
var compile = function(str){
var tpl = str.replace(/<%=([\s\S]+?)%>/,function(match, p1){
return 'obj.' + p1;
}); // 得到 obj.username tpl = 'return ' + tpl; // return obj.username
return new Function('obj',tpl); //function(obj){return obj.username}
}
var compiled = compile(str);
var rs = compiled(data); // Muhha

缺点很明显 只能对<%=obj.xxx%>这样的部分作替换, 若模板变成<p><%=username%></p> 这样就不能通过

第二版

上面有缺陷的原因就在于替换的之后只考虑了呗替换的部分, 那些没有被替换的部分 比如 <p> 没有考虑

var data = {
username: 'Muhha'
}
str = '<p><%=username%></p>';
var compile = function(str){
var tpl = str.replace(/<%=([\s\S]+?)%>/gi,function(match, p1){
return '\'+' + 'obj.' + p1 + '+\'';
}); // 得到 <p>' + obj.username + '</p> tpl = 'return ' + '\'' +tpl + '\''; //return '<p>' + obj.username + '</p>'
return new Function('obj',tpl); //function(obj){return '<p>' + obj.username + '</p>'}
}
var compiled = compile(str);
var rs = compiled(data); // <p>Muhha</p>

多加一个属性

var data = {
username: 'Muhha',
age: 12
}
str = '<p><%=username%></p><p><%=age%></p>';
var compile = function(str){
var tpl = str.replace(/<%=([\s\S]+?)%>/gi,function(match, p1){
return '\'+' + 'obj.' + p1 + '+\'';
}); // 得到 <p>' + obj.username + '</p><p> '+ obj.age + '</p> tpl = 'return ' + '\'' +tpl + '\''; //return '<p>' + obj.username + '</p><p>'+ obj.age + '</p>'
return new Function('obj',tpl); //return '<p>' + obj.username + '</p><p>'+ obj.age + '</p>'}
}
var compiled = compile(str);
var rs = compiled(data); // <p>Muhha</p><p>12</p>

不过这仍然是有缺陷的, 比如不能使用语句

当模板写成str = 'if(gender>1){<p><%=username%></p>}<p><%=age%></p>'; 这样时

得到的结果是if(gender>1){<p>Muhha</p>}<p>12</p>

显然这对逻辑没有解析, 原因就在于return太早了

把整个模板当成了一个结果return了

我们期待的模板函数是这样的

var tpl = '';
if(gender ==1){
tpl +='<p>'+ obj.gender +'</p>';
}
tpl += '<p>' + obj.age +'</p>';
return tpl;

第三版

var data = {
username: 'Muhha',
age: 12,
gender: 1
}
str = [
'<p><%=obj.username%></p>',
'<%if(obj.gender==1){%>',
'<p><%=obj.gender%></p>',
'<%}%>',
'<p><%=obj.age%></p>'].join('\n');
var compile = function(str){
tpl = str.replace(/\n/g, '')
//替换变量
.replace(/<%=([\s\S]+?)%>/gi,function(match, p1){
return [
'\'+',
p1,
'+\''
].join(''); // <%=obj.username%> 变成 ' + obj.username + '
})
//替换执行语句
.replace(/<%([\s\S]+?)%>/g, function(match, p1){
return [
'\';',
'\n',
p1,
'\n',
'tpl+=\''
].join(''); // if(a){ 变成 ';\n if(a){ tpl+= '
}) tpl = '\'' +tpl + '\'';
//此时tpl
// '<p>'+obj.username+'</p>';
// if(obj.gender==1){
// tpl+='<p>'+obj.gender+'</p>';
// }
// tpl+='<p>'+obj.age+'</p>' tpl = 'var tpl="";\ntpl+=' + tpl;
tpl = tpl + ';\nreturn tpl;';
return new Function('obj',tpl);
}
var compiled = compile(str);
var rs = compiled(data); // <p>Muhha</p><p>1</p><p>12</p>

这样还是有缺陷的, 比如某个字段的值为<script>...</script> 这样的话就会向页面中插入了可执行的标签

第四版

<script type="text/template" id="temp">
<p><%=obj.username%></p>
<%if(obj.gender==1){%>
<p><%=obj.gender%></p>
<%}%>
<p><%=obj.age%></p>
</script>

这一版还将模板放在了script标签中

var data = {
username: '\<script\>Muhha\<\/script\>',
age: 12,
gender: 1
}
var escape = function(str){
str = '' + str;
str= str.replace(/&(?!\w+)/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039'); // '&#039' 就是单引号 &apos; 这样写是因为IE不支持 &apos;
return str;
}
var compile = function(str){
tpl = str
.replace(/\n/g, '')
.replace(/\s/g, '')
//替换变量
.replace(/<%=([\s\S]+?)%>/gi,function(match, p1){
return [
'\'+',
'escape(' + p1 + ')',
'+\''
].join(''); // <%=obj.username%> 变成 ' + obj.username + '
})
//替换执行语句
.replace(/<%([\s\S]+?)%>/g, function(match, p1){
return [
'\';',
'\n',
p1,
'\n',
'tpl+=\''
].join(''); // if(a){ 变成 ';\n if(a){ tpl+= '
}) tpl = '\'' +tpl + '\'';
//此时tpl
// '<p>'+obj.username+'</p>';
// if(obj.gender==1){
// tpl+='<p>'+obj.gender+'</p>';
// }
// tpl+='<p>'+obj.age+'</p>' tpl = 'var tpl="";\ntpl+=' + tpl;
tpl = tpl + ';\nreturn tpl;';
return new Function('obj, escape',tpl);
//得到
//(function(obj, escape
// /**/) {
// var tpl="";
// tpl+='<p>'+escape(obj.username)+'</p>';
// if(obj.gender==1){
// tpl+='<p>'+escape(obj.gender)+'</p>';
// }
// tpl+='<p>'+escape(obj.age)+'</p>';
// return tpl;
// })
}
var compiled = compile(document.querySelector('#temp').text); //script标签有text属性 但是其他标签只能用textContent innerText
var rs = compiled(data, escape); // <p>&lt;script&gt;Muhha&lt;/script&gt;</p><p>1</p><p>12</p>

一个简单的模板引(han)擎(shu)的更多相关文章

  1. [转贴]从零开始学C++之STL(二):实现一个简单容器模板类Vec(模仿VC6.0 中 vector 的实现、vector 的容量capacity 增长问题)

    首先,vector 在VC 2008 中的实现比较复杂,虽然vector 的声明跟VC6.0 是一致的,如下:  C++ Code  1 2   template < class _Ty, cl ...

  2. 1、编写一个简单Makefile模板

    一.Makefile简介 一个工程中的源文件不计其数,其按类型.功能.模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译, ...

  3. Java实现的一个简单的模板渲染

    代码 package com.hdwang; import java.util.HashMap; import java.util.Map; /** * Created by hdwang on 20 ...

  4. 一个简单的模板了解css+div网页布局

    直接附上最终效果图: index.html内容: <html> <!--20170730 soulsjie--> <head> <meta http-equi ...

  5. OpenCms JSP 模板开发——创建一个简单的JSP模板

    OpenCms中的JSP模板就是一个普通的JSP页面,在特定的位置使用标签来包含内容,在这个的例子中,我们将要开发一个简单JSP模板,这个模板只是在内容(如<html>.<body& ...

  6. 如何设计Java框架----一个简单的例子【翻译】

    原文:http://www.programcreek.com/2011/09/how-to-design-a-java-framework/ 原文和翻译都只是参考,如有不对,欢迎指正. 你可能会好奇框 ...

  7. 用EF DataBase First做一个简单的MVC3报名页面

    使用EF DataBase First做一个简单的MVC3报名网站 ORM(Object Relational Mapping)是面向对象语言中的一种数据访问技术,在ASP.NET中,可以通过ADO. ...

  8. 创建一个简单的terraform module

      terraform module可以实现代码的复用,同时方便分享,下面创建一个简单的基于localfile && template provider 的module module ...

  9. 手把手教你从零写一个简单的 VUE--模板篇

    教程目录1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 Hello,我又回来了,上一次的文章教会了大家如何书写一个简单 VUE,里面实现了VUE 的数据驱动视图 ...

随机推荐

  1. 用SQL脚本移除视图中存在的机器名

    用SQL脚本移除视图中存在的机器名 例子: msccdr.cdr.DimRMAReturnMethod CREATE VIEW CDR.DimRMAReturnMethod ( ReturnMetho ...

  2. 控制台下的计算器——C++实现

    最近这段时间看操作系统的东西看的头晕脑胀的,所以先停个一晚上,写个控制台下的计算器,来练练栈的使用. 首先,分析一下要完成的东西.输入肯定使用string类来输入的,然后第一步,就是把string的字 ...

  3. 无法下载图片 App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file

    刚学线程通信,提示: 2016-01-27 11:11:02.246 20-9 gcd3 communicationOfThread[5193:298643] App Transport Securi ...

  4. Linux 学习之防火墙配置

    1.安装iptables防火墙 yum install iptables  2. 清除已有的iptables规则  iptables -F  iptables -X  iptables -Z  3.显 ...

  5. Beyond Compare设置默认为ANSI格式

    工具 -> 文件格式 -> 选中C,C++,... -> 转换 -> 外部程序(ANSI文件名) 且 编码(选“ANSI”)-> 保存 -> 关闭

  6. struts2中的表达元素标签使用详解

    级联标签是使用:一级下拉框应该使用map对象的key集合作为下拉框元素,二级下了框应该使用一级下拉框对应的选择值自动的弹出待选择的元素值(集合) 页面代码如下:<s:set name=" ...

  7. 【Chromium中文文档】进程模型

    进程模型 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Process_ ...

  8. html中上标、下标、删除字、小号字等

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. Cloning Java objects using serialization

    Sometimes you need to clone objects, and sometimes you can't use their clone method, and sometimes s ...

  10. CSharp tar类型文件压缩与解压

    最近闲暇时间开始写点通用基础类在写到tar类型文件压缩与解压时遇到点问题 压缩用的类库我是下载的 SharpZipLib_0860版本 先上代码 加压核心 /// <summary> // ...