一个简单的模板引(han)擎(shu)
自制一个简单的模板引(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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, '''); // ''' 就是单引号 ' 这样写是因为IE不支持 '
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><script>Muhha</script></p><p>1</p><p>12</p>
一个简单的模板引(han)擎(shu)的更多相关文章
- [转贴]从零开始学C++之STL(二):实现一个简单容器模板类Vec(模仿VC6.0 中 vector 的实现、vector 的容量capacity 增长问题)
首先,vector 在VC 2008 中的实现比较复杂,虽然vector 的声明跟VC6.0 是一致的,如下: C++ Code 1 2 template < class _Ty, cl ...
- 1、编写一个简单Makefile模板
一.Makefile简介 一个工程中的源文件不计其数,其按类型.功能.模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译, ...
- Java实现的一个简单的模板渲染
代码 package com.hdwang; import java.util.HashMap; import java.util.Map; /** * Created by hdwang on 20 ...
- 一个简单的模板了解css+div网页布局
直接附上最终效果图: index.html内容: <html> <!--20170730 soulsjie--> <head> <meta http-equi ...
- OpenCms JSP 模板开发——创建一个简单的JSP模板
OpenCms中的JSP模板就是一个普通的JSP页面,在特定的位置使用标签来包含内容,在这个的例子中,我们将要开发一个简单JSP模板,这个模板只是在内容(如<html>.<body& ...
- 如何设计Java框架----一个简单的例子【翻译】
原文:http://www.programcreek.com/2011/09/how-to-design-a-java-framework/ 原文和翻译都只是参考,如有不对,欢迎指正. 你可能会好奇框 ...
- 用EF DataBase First做一个简单的MVC3报名页面
使用EF DataBase First做一个简单的MVC3报名网站 ORM(Object Relational Mapping)是面向对象语言中的一种数据访问技术,在ASP.NET中,可以通过ADO. ...
- 创建一个简单的terraform module
terraform module可以实现代码的复用,同时方便分享,下面创建一个简单的基于localfile && template provider 的module module ...
- 手把手教你从零写一个简单的 VUE--模板篇
教程目录1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 Hello,我又回来了,上一次的文章教会了大家如何书写一个简单 VUE,里面实现了VUE 的数据驱动视图 ...
随机推荐
- java 线程之间通信以及notify与notifyAll区别。
jvm多个线程间的通信是通过 线程的锁.条件语句.以及wait().notify()/notifyAll组成. 下面来实现一个启用多个线程来循环的输出两个不同的语句. package com.app. ...
- TCP/IP的三次握手协议
关于TCP/IP的三次握手协议,这篇文章中有详细的介绍,很通俗易懂,什么时候忘了,都可以过来瞧两眼,保证很快就明白了. 首先TCP/IP协议分为三个阶段:建立连接(握手阶段),数据传输阶段,连接终止阶 ...
- struts2 + jquery 开发环境下的ajax构建方法(action写法 + struts.xml配置 + js调用代码)
1.action写法 public class RegisterAction extends ActionSupport { private InputStream inputStream; /** ...
- APサーバ
GlassFish GlassFish 是一款强健的商业兼容应用服务器,达到产品级质量,可免费用于开发.部署和重新分发. 简介 GlassFish 是用于构建 Java EE 5应用服务器的开源开发项 ...
- codeforces 15D . Map 优先队列
题目链接 题目意思很简单nm的矩阵里, 选若干个ab的小矩阵, 定义每个矩阵的值为这个矩阵里的所有数的和-最小值*数的个数. 选小矩阵时, 优先选值最小的,然后次小的.. 知道不能选位置. 输出所有矩 ...
- Qt窗口的标题栏自绘
因个人需要,要修改Qt Widget的标题栏,网上找了大半天,没有得到答案,但发现问的人比较多 所以现将找到的此文分享一下. (原文:http://www.qtsoftware.com/develop ...
- Qt中各个widget前后位置的设定(在Qt中,所有问题都要一分为二,QWidget体系和QGraphicsWidget体系)
这两天在总结一些以往project中遇到的问题,正好别组有同事问我关于Qt中各个widget窗口的前后位置是如何定义的,这里就总结一下: 在Qt中,所有问题都要一分为二,讨论两种不同的情况:一个是最常 ...
- isequal 和startswith 使用
如果要把不同服务器发送过来的日志保存到不同的文件, 可以这样操作: :fromhost-ip, isequal, "192.168.0.160″ /var/log/host160.log : ...
- mysql timestamp 值不合法问题
Create Table: CREATE TABLE `RecruitmentDesc` ( `sn` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号(自增字段 ...
- Openstack service default port
Block Storage (cinder) 8776 publicurl and adminurl Compute API (nova-api) 8773 EC2 API 8774 openstac ...