自制一个简单的模板引(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. java 线程之间通信以及notify与notifyAll区别。

    jvm多个线程间的通信是通过 线程的锁.条件语句.以及wait().notify()/notifyAll组成. 下面来实现一个启用多个线程来循环的输出两个不同的语句. package com.app. ...

  2. TCP/IP的三次握手协议

    关于TCP/IP的三次握手协议,这篇文章中有详细的介绍,很通俗易懂,什么时候忘了,都可以过来瞧两眼,保证很快就明白了. 首先TCP/IP协议分为三个阶段:建立连接(握手阶段),数据传输阶段,连接终止阶 ...

  3. struts2 + jquery 开发环境下的ajax构建方法(action写法 + struts.xml配置 + js调用代码)

    1.action写法 public class RegisterAction extends ActionSupport { private InputStream inputStream; /** ...

  4. APサーバ

    GlassFish GlassFish 是一款强健的商业兼容应用服务器,达到产品级质量,可免费用于开发.部署和重新分发. 简介 GlassFish 是用于构建 Java EE 5应用服务器的开源开发项 ...

  5. codeforces 15D . Map 优先队列

    题目链接 题目意思很简单nm的矩阵里, 选若干个ab的小矩阵, 定义每个矩阵的值为这个矩阵里的所有数的和-最小值*数的个数. 选小矩阵时, 优先选值最小的,然后次小的.. 知道不能选位置. 输出所有矩 ...

  6. Qt窗口的标题栏自绘

    因个人需要,要修改Qt Widget的标题栏,网上找了大半天,没有得到答案,但发现问的人比较多 所以现将找到的此文分享一下. (原文:http://www.qtsoftware.com/develop ...

  7. Qt中各个widget前后位置的设定(在Qt中,所有问题都要一分为二,QWidget体系和QGraphicsWidget体系)

    这两天在总结一些以往project中遇到的问题,正好别组有同事问我关于Qt中各个widget窗口的前后位置是如何定义的,这里就总结一下: 在Qt中,所有问题都要一分为二,讨论两种不同的情况:一个是最常 ...

  8. isequal 和startswith 使用

    如果要把不同服务器发送过来的日志保存到不同的文件, 可以这样操作: :fromhost-ip, isequal, "192.168.0.160″ /var/log/host160.log : ...

  9. mysql timestamp 值不合法问题

    Create Table: CREATE TABLE `RecruitmentDesc` ( `sn` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号(自增字段 ...

  10. Openstack service default port

    Block Storage (cinder) 8776 publicurl and adminurl Compute API (nova-api) 8773 EC2 API 8774 openstac ...