最简单的JavaScript模板引擎
在小公司待久了感觉自己的知识面很小,最近逛博客园和一些技术网站看大家在说JavaScript模版引擎的事儿,完全没有概念,网上一搜这是08年开始流行起来的。。。本来以为这是很高深的知识,后来在网上看到jQuery作者John Resig,研究了一下,算是明白了最简单的javaScript模版引擎的原理,并没有想象的那么高大上,写篇博客推导一下John Resig写法的过程,写出一个最简单的JavaScript模版引擎。
什么是JavaScript引擎
其实在网站开发中模板还是很常见的一种技术,比如PHP的Smarty、ASP.NET的Master Page等,但这些模板都是基于服务器的,JavaScript模板引擎是为了解决我们在前端写出形如这样的拼html的语句
var html='<ul>';
for(var i=0;i<users.length;i++){
html+='<li><a href=">'+users[i].url+'">'+users[i].name+'</a>';
}
html+='</ul>'; document.getElementById('results').innerHTML=html;
上面的代码我们一看就知道是在拼html,但具体拼的什么很难说清,需要逐句去读代码,如果我们有这样一个模板
<ul>
<% for ( var i = 0; i < users.length; i++ ) { %>
<li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>
<% } %>
</ul>
看了很容易就明白开发者希望得到的是这样的html
<ul>
<li><a href="XXX">OOO</a></li>
<li><a href="XXX">OOO</a></li>
<li><a href="XXX">OOO</a></li>
</ul>
JavaScript模板引擎就是帮我们把带有JavaScript代码的伪html语句翻译为html的东东
John Resig的实现方式
先看看John Resig是怎么实现最简单的一个JavaScript模板引擎的
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
var cache = {}; this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) : // Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" + // Introduce the data as local variables using with(){}
"with(obj){p.push('" + // Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');"); // Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();
看完上面代码就明白的同学就不用看下面内容了,没太明白的同学可以和我一块儿看看着三十多句代码为什么能够实现一个JavaScript引擎吧。
模板的语法
模板的语法很简单,有三条基本规则
- 用正常的方式书写html
- 用<% %>嵌套JavaScript语句
- 用<%= %>嵌套JavaScript 变量值
模板转换为html字符串原理
我们的JavaScript引擎正式设计为识别这种类型的模板的,拿上面的做例子,这样的一个模版
<ul>
<% for ( var i = 0; i < users.length; i++ ) { %>
<li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>
<% } %>
</ul>
想得到预期html字符串,我们必须设法让模板内部的javascript变量置换、javaScript语句执行,也就是把JavaScript代码剥离出来执行,把其它html语句拼接为一个字符串
var p=[];
p.push('<ul>');
for(var i=0;i<users.length;i++){ //javascript语句执行
p.push('<li><a href="'); //html语句拼接
p.push(users[i].url); //javascript变量置换后拼接
p.push('">');
p.push(users[i].name);
p.push('</a></li>');
}
p.push('</ul>');
最后得到的数组join一下就是我们希望得到的字符串了,首先需要取到模板内的字符串,这个简单按照John的做法我们可以把模板放到一个script标签里(防止在页面显示出来),换成我们特定的类型
<script type="text/html" id="user_tmpl">
<ul>
<% for ( var i = 0; i < users.length; i++ ) { %>
<li>
<a href="<%=users[i].url%>">
<%=users[i].name%>
</a>
</li>
<% } %>
</ul>
</script>
这样就可以通过 document.getElementById(str).innerHTML 来获取模版内字符串了,然后我们应用一些简单的法则处理一下模板内字符串
<%=xxx%> => ');p.push(xxx);p.push('
<% => ');
%> => p.push('
这样我们就可以得到这样的结构,看起来就已经很接近结果了
p.push('<ul>');
for(var i=0;i<users.length;i++){
p.push('<li><a href="');
p.push(users[i].url);
p.push('">');
p.push(users[i].name);
p.push('</a></li>');
}
p.push('</ul>');
简单的字符串置换
现在我们根据上面规则做替换了,这里得使用一些正则表达式和replace函数的知识,不太熟悉的同学可能需要看看 JavaScript 正则表达式上——基本语法 JavaScript正则表达式下——相关方法
1.把<%=xxx%> 替换为 ');p.push(xxx);p.push('
html=html.replace(/<%=(.*?)%>/g,"');p.push(xxx);p.push('");
2.把<%替换为 ');
html=html.replace(/<%/g,"');");
3.把%> 替换为 p.push('
html=html.replace(/%>/g,"p.push('");
我们再把结果用p.push(' 和 '); 包裹起来就可以看到初步效果了
function tmpl(id,data){
var html=document.getElementById(id).innerHTML;
var result="var p=[]; p.push('"
+html.replace(/<%=(.*?)%>/g,"');p.push(xxx);p.push('")
.replace(/<%/g,"');")
.replace(/%>/g,"p.push('")
+" ');";
}
这样我们就把html模版内容替换成了这样的一个字符串
var result="
var p=[];
p.push('<ul>');
for(var i=0;i<users.length;i++){
p.push('<li><a href="');
p.push(users[i].url);
p.push('">');
p.push(users[i].name);
p.push('</a></li>');
}
p.push('</ul>');"
貌似得到结果了,但我们得到的是字符串,我们预期的是这个字符串执行的结果,很多同学会想到使用eval就可以让字符串变成JavaScript语句执行,但是Jonh使用了另外一种方式——创建function,我们知道除了常用使用function关键字创建一个function
function fn(data){
console.log(data);
}
还可以使用Function构造函数来创建一个function
var fn = new Function(arg1, arg2, ..., argN, function_body)
在上面的形式中,每个 arg 都是一个参数,最后一个参数是函数主体(要执行的代码),使用这种方式可以动态(方法体是动态生成的,提前不知道,当然这样做会有效率问题)创建一个方法,也就是说我们还可以使用刚才拼出来的javascript字符串动态创建一个函数
function tmpl(id,data){
var html=document.getElementById(id).innerHTML;
var result="var p=[];p.push('"
+html.replace(/[\r\n\t]/g," ")
.replace(/<%=(.*?)%>/g,"');p.push($1);p.push('")
.replace(/<%/g,"');")
.replace(/%>/g,"p.push('")
+"');return p.join('');";
var fn=new Function(data,result);
return fn(data);
}
这样看起来很科学了,但是我们执行一下会报错,原因很简单就是参数的作用域不对,我们需要改变一下动态构造的方法的作用域,这个有很多方式比如apply函数啊什么的,我们暂且采用John的方式——使用with关键字改变作用域
function tmpl(id,data){
var html=document.getElementById(id).innerHTML;
var result="var p=[];with(obj){p.push('"
+html.replace(/[\r\n\t]/g," ")
.replace(/<%=(.*?)%>/g,"');p.push($1);p.push('")
.replace(/<%/g,"');")
.replace(/%>/g,"p.push('")
+"');}return p.join('');";
var fn=new Function("obj",result);
return fn(data);
}
虽然看起来和John的方法还有很大区别,不过我们已经偷师到了其精髓,实现了一个最简单JavaScript模版引擎,你是不是也明白了JavaScript模版引擎是什么了呢?就是简单的字符串替换,剥离出JavaScript语句,然后利用新的字符串构造函数,返回结果。
看个例子
<!DOCTYPE html>
<html>
<head>
<title>Template</title>
</head>
<body> <div id="results"></div> <script type="text/html" id="user_tmpl">
<ul>
<% for ( var i = 0; i < users.length; i++ ) { %>
<li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>
<% } %>
</ul>
</script> <script type="text/javascript">
var results = document.getElementById("results");
var users=[
{"name":"Byron", "url":"http://localhost"},
{"name":"Casper", "url":"http://localhost"},
{"name":"Frank", "url":"http://localhost"}
]; function tmpl(id,data){
var html=document.getElementById(id).innerHTML;
var result="var p=[];with(obj){p.push('"
+html.replace(/[\r\n\t]/g," ")
.replace(/<%=(.*?)%>/g,"');p.push($1);p.push('")
.replace(/<%/g,"');")
.replace(/%>/g,"p.push('")
+"');}return p.join('');";
var fn=new Function("obj",result);
return fn(data);
} results.innerHTML = tmpl("user_tmpl", users);
</script>
</body>
</html>
应用了简单的JavaScript模版引擎,我们可以很方便的拼出一些html了

参考
John Resig JavaScript Micro-Templating汤姆大叔 大叔手记(7):构建自己的JavaScript模板小引擎 BarretLee JavaScript模板引擎原理,几行代码的事儿 PS. 目前写的JavaScript模版引擎还有很多性能、特殊情况处理问题,下篇博客(简单JavaScript模版引擎优化)会继续进行一些优化工作
最简单的JavaScript模板引擎的更多相关文章
- 转载---最简单的JavaScript模板引擎
转载自:http://www.cnblogs.com/dolphinX/p/3489269.html,http://blog.jobbole.com/56689/
- 推荐13款javascript模板引擎
javaScript 在生成各种页面内容时如果能结合一些模板技术,可以让逻辑和数据之间更加清晰,本文介绍 X 款 JavaScript 的模板引擎.(排名不分先后顺序) 1. Mustache 基于j ...
- JavaScript模板引擎实例应用
在之前的一篇名为<移动端基于HTML模板和JSON数据的JavaScript交互>的文章中,我向大家说明了为什么要使用JavaScript模板以及如何使用,文末还提到了laytpl.art ...
- JavaScript 模板引擎实现原理解析
1.入门实例 首先我们来看一个简单模板: <script type="template" id="template"> <h2> < ...
- Javascript模板引擎mustache.js详解
mustache.js是一个简单强大的Javascript模板引擎,使用它可以简化在js代码中的html编写,压缩后只有9KB,非常值得在项目中使用.本文总结它的使用方法和一些使用心得,内容不算很高深 ...
- 如何选择Javascript模板引擎(javascript template engine)?
译者 jjfat 日期:2012-9-17 来源: GBin1.com 随着前端开发的密集度越来越高,Ajax和JSON的使用越来越频繁,大家肯定免不了在前台开发中大量的使用标签,常见到的例子如下: ...
- 【JavsScript】推荐五款流行的JavaScript模板引擎
摘要:Javascript模板引擎作为数据与界面分离工作中最重要一环,受到开发者广泛关注.本文通过开发实例解析五款流行模板引擎:Mustache.Underscore Templates.Embedd ...
- 高性能JavaScript模板引擎原理解析
随着 web 发展,前端应用变得越来越复杂,基于后端的 javascript(Node.js) 也开始崭露头角,此时 javascript 被寄予了更大的期望,与此同时 javascript MVC ...
- JavaScript模板引擎
JavaScript模板引擎实例应用 在之前的一篇名为<移动端基于HTML模板和JSON数据的JavaScript交互>的文章中,我向大家说明了为什么要使用JavaScript模板以及 ...
随机推荐
- IOS RunLoop浅析 三
经过两篇的介绍我想对RunLoop应该有了简单的了解,至少不至于一无所知. 在这篇我想对“CFRunLoopObserverRef”做一下简单的补充. 在补充之前先说一下. 在现在的开发中已经很少见到 ...
- Python之基础
# 需要导入字符编码,否则遇到中文会报错 # coding=utf-8 # 1 定义变量 a = 10 b = 2 c = a+b print(c) # 2 判断语句 score = 90 if sc ...
- php 日期计算 总结
1 加 2天 date("Y-m-d", strtotime("$today + 2 days")); date("Y-m-d", strt ...
- configure Git to accept a particular self-signed server certificate for a particular https remote
get the self signed certificate put it into some (e.g. ~/git-certs/cert.pem) file set git to trust t ...
- #VSTS日志# TFS 2015 Update 2 RC2新功能
有段时间没有更新#VSTS日志#了,最近小编太忙,全国各地飞来飞去给各种不同的团队实施敏捷,今天冷不丁一看,呀!TFS 2015 Update 2 RC2都已经发布了.里面好东西不少,列出几个给大家瞧 ...
- Linux系统安装Apache 2.4.6
Apache简介 Apache HTTP Server(简称Apache)是Apache软件基金会的一个开放源码的网页服务器,可以在大多数计算机操作系统中运行,由于其多平台和安全性被广 ...
- asp.net signalR 专题—— 第二篇 对PersistentConnection持久连接的快速讲解
上一篇我们快速的搭建了一个小案例,但是并没有对其中的方法进行介绍,这一篇我来逐一解析下. 一:从override的那些方法说起 不管怎么样,我们先上代码,如下: public class MyConn ...
- Oracle触发器原理、创建、修改、删除
本篇主要内容如下: 8.1 触发器类型 8.1.1 DML触发器 8.1.2 替代触发器 8.1.3 系统触发器 8.2 创建触发器 8.2.1 触发器触发次序 8.2.2 创建DML触发器 8.2. ...
- sql server 2000,Log.LDF文件丢失,附加数据库失败的解决办法[转]
SQL Server数据库备份有两种方式,一种是使用BACKUP DATABASE将数据库文件备份出去,另外一种就是直接拷贝数据库文件mdf和日志文件ldf的方式.下面将主要讨论一下后者的备份与恢复. ...
- linux c++应用程序内存高或者占用CPU高的解决方案_20161213
对于绝大多数实时程序来说,实时处理相关程序中的循环问题所带来的对机器的损耗和自身的处理速度的平衡,以及与其他程序的交互以及对其他功能的影响难免会成为程序设计中最大的障碍同时也是最大的突破点. 在所有这 ...