自制一个简单的模板引(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. Centos6.5源码编译安装nginx

    1.安装pcre下载地址:http://jaist.dl.sourceforge.net/project/pcre/pcre/8.38/pcre-8.38.tar.gz #tar -axvf pcre ...

  2. artdialog关闭弹出窗口

    打开 function opentree(){  var dialog = art.dialog({    title: '选择提交部门',      content:jQuery("#my ...

  3. org.gradle.process.internal.ExecException:

    com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process ...

  4. APP分享抓取网页图片

    var getShareImages = { defaultimg:"defaultimg.png", _allImgs:null, init:function(){ getSha ...

  5. javaScript的2种变量范围有什么不同

    1.javascript怎样选中一个checkbox,怎样设置它无效? document.all.cb1[0].disabled = true;   2.js中的3种弹出式消息提醒(警告窗口,确认窗口 ...

  6. php获取网页内容方法 小偷程序 采集程序

    抓取到的内容在通过正则表达式做一下过滤就得到了你想要的内容,至于如何用正则表达式过滤,在这里就不做介绍了,有兴趣的,以下就是几种常用的用php抓取网页中的内容的方法.1.file_get_conten ...

  7. React和Angular

    React和Angular 你若装逼,请带我飞! 从前,从前,听说React只负责UI,话说写Angular代码就像写后端,现在看来,React赢在情怀上了: 我认为没必要老是拿React和Angul ...

  8. easyui datagrid 列的内容超出所定义的列宽时,自动换行

    定义表单  nowrap="false"可以使得列中的内容超出所定义的列宽是就会自动换行pagination : true, // 当true时在DataGrid底部显示一个分页工 ...

  9. 查询linux发行版本号方法总结

      了解Linux发行版本的版本号是一项非常重要的事情,大多数软件对系统的版本都有要求,发行版本号与软件不匹配,软件将无法安装或者无法使用.这边集合市面上流行的Linux发行版本版本号查询方法.有了这 ...

  10. 无线功率 mW 和 dBm 的换算

    无线电发射机输出的射频信号,通过馈线(电缆)输送到天线,由天线以电磁波形式辐射出去.电磁波到达接收地点后,由天线接收下来(仅仅接收很小很小一部分功率),并通过馈线送到无线电接收机.因此在无线网络的工程 ...