本文给出一个通过组合使用JST,JSON和AJAX技术,使用客户端js模版代替服务端数据绑定的范例。很显然的,使用客户端数据绑定代替服务端数据绑定能够大大减少服务端的内存和CPU消耗,在硬件不变的情况下,大大提升服务器负载能力。
名词解释
首先,先简单介绍一下JST和JSON。都直接引用官方介绍了。
JST (JavaScript Templates)
For web application developers, the JavaScript Templates engine from TrimPath is a lightweight APL / GPL open-source component that lets you have template-based programming (like PHP/ASP/JSP) while running in a web browser.
- The JST engine is written entirely in standard JavaScript.
- It supports a productive template markup syntax very much like FreeMarker, Velocity, Smarty.
- JST is a readable alternative to manually coded massive String concatenations and major DOM/DHTML manipulations. 、
JSON (JavaScript Object Notation)
JSON is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.
简单的说,JST是一个类似Velocity的模版引擎,只不过它是纯js的。而JSON是一个类似XML的数据交换语言,它使用了js语言的一个子集来定义数据,因此,对js而言,JSON格式的数据可以作为一个对象使用,甚至完全无须额外解析,也比XML体积更小,一般来说,它的序列化后大小只有XML序列化的一半,当然,它也是纯js的。
AJAX就不多说了。
范例简介及下载
本范例采用Web Application Architectures: Simple 2 Layer, Standard 3 Layer and Distributed 3 Layer一文中的Simple 2 Layer构架,只是在Web表现层使用JST,JSON和AJAX代替原来的直接使用ASP.NET数据绑定。
您可以访问https://sourceforge.net/projects/nbear/下载最新的NBear v2.0.1,其中包含了本文讨论的范例的全部源码(见zip包中的sample目录下)和所依赖的NBear框架的全部源码。(运行范例前请注意修改web.config中的TestAccessDb的数据库连接字串。)
范例分析
如果您对Simple 2 Layer构架没有概念,请先访问Web Application Architectures: Simple 2 Layer, Standard 3 Layer and Distributed 3 Layer一文中的相关介绍。
让我们先从JST开始说起。
要让我们的Web程序能够调用JST的类库,我们只需要将template.js文件包含到当前页面。该文件的最新版本可以从http://trimpath.com/project/wiki/JavaScriptTemplates下载。
首先,看看我们的,Default.aspx文件
代码 Default.aspx
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
2![](/Images/OutliningIndicators/None.gif)
3
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4![](/Images/OutliningIndicators/None.gif)
5
<html xmlns="http://www.w3.org/1999/xhtml" >
6
<head runat="server">
7
<title>Untitled Page</title>
8
<script language="javascript" type="text/javascript" src="js/template.js"></script>
9![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
<script language="javascript" type="text/javascript">![](/Images/dot.gif)
10
//bind specified js template - jst with data template - dt
11
function LoadTemplateData(jst, dt, paramsToDt, processHandler)
12![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
13
Ajax.Callback(dt, 'jst=' + jst + '&' + paramsToDt, processHandler);
14
}
15
16
function DoBinding1(content)
17![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
18
//alert(content);
19
20
var data = eval("(" + content + ")");
21
22
var result = TrimPath.parseTemplate(data.jst).process(data);
23
24
document.getElementById('result').innerHTML = result;
25
}
26
</script>
27
</head>
28
<body onload="LoadTemplateData('JsTemplates/DemoTemplate', 'AjaxTemplates/DemoData', null, 'DoBinding1');">
29
<form id="form1" runat="server">
30
<div>
31
<input type="button" value="Load Demo Data 2" onclick="LoadTemplateData('JsTemplates/DemoTemplate2', 'AjaxTemplates/DemoData2', null, 'DoBinding1');"/><br />
32
<br />
33
</div>
34
<div id="result">
35
</div>
36
</form>
37
</body>
38
</html>
在以上代码中,除了可以载入template.js的script标签,还有一句和JST相关的代码是第22行:
var result = TrimPath.parseTemplate(data.jst).process(data);
该行代码调用了一组JST类库的函数,将一个js的对象data,通过一个JST模版data.jst(一个JST模版是一段字符串模版文本)进行解析,并返回解析转换后的html内容。这个过程有点类似xml/xslt。这里的data.jst相当于xslt而data相当于xml。
Default.aspx中的其他内容也比较简单,在pageload和点击按钮时,通过调用LoadTemplateData()方法-〉Ajax.Callback()方法,执行一个Ajax回调。这里的Ajax实现,是用内置于NBear的AjaxHelper组件来实现,关于该Ajax组件的更多介绍可以参见Call Back Callback - 整合AjaxHepler到NBear一文。在本文中,您只需要了解,Ajax.Callback()方法我们可以很方便的执行一个Ajax回调,并返回一个User Control中的内容。
我想你已经猜到了,我们JSON数据和JST模版,都可以通过User Control来定义。
以页面pageload时载入的数据为例,他用到了一个AjaxTemplate:DemoData.ascx和一个JsTemplate:DemoTemplate.ascx。
代码 DemoData.ascx
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
<%
@ Control Language="C#" AutoEventWireup="true" CodeFile="DemoData.ascx.cs" Inherits="AjaxTemplates_DemoData" %>
2
{
3
products : [ { name: "mac", desc: "computer",
4
price: 1000, quantity: 100, alert:null },
5
{ name: "ipod", desc: "music player",
6
price: 200, quantity: 200, alert:"on sale now!" },
7
{ name: "cinema display", desc: "screen",
8
price: 800, quantity: 300, alert:"best deal!" } ],
9
customer : { first: "John", last: "Public", level: "gold" },
10
11
jst : '<%= jstContent %>'
12
}
代码 DemoData.ascx.cs
1
using System;
2
using System.Data;
3
using System.Configuration;
4
using System.Text;
5
using System.Collections;
6
using System.Web;
7
using System.Web.Security;
8
using System.Web.UI;
9
using System.Web.UI.WebControls;
10
using System.Web.UI.WebControls.WebParts;
11
using System.Web.UI.HtmlControls;
12![](/Images/OutliningIndicators/None.gif)
13
public partial class AjaxTemplates_DemoData : NBear.Web.UI.AjaxTemplate
14![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
15
protected string jstContent;
16![](/Images/OutliningIndicators/InBlock.gif)
17
public override void OnAjaxTemplatePreRender(System.Collections.Generic.Dictionary<string, string> callbackParams)
18![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
19
jstContent = LoadJstContentFromUserControl("../" + callbackParams["jst"]).Replace("/r/n", "//n");
20
}
21
}
代码 DemoTemplate.ascx
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
<%
@ Control Language="C#" AutoEventWireup="true" CodeFile="DemoTemplate.ascx.cs" Inherits="JsTemplates_DemoTemplate" %>
2
Hello ${customer.first} ${customer.last}.<br/>
3
Your shopping cart has ${products.length} item(s):
4
<table>
5
<tr><td>Name</td>
6
<td>Description</td>
7
<td>Price</td>
8
<td>Quantity & Alert</td>
9
</tr>
10
{for p in products}
11
<tr><td>${p.name|capitalize}</td><td>${p.desc}</td>
12
<td>$${p.price}</td><td>${p.quantity} :
13
${p.alert|default:""|capitalize}</td>
14
</tr>
15
{forelse}
16
<tr><td colspan="4">No products in your cart.</tr>
17
{/for}
18
</table>
19
{if customer.level == "gold"}
20
We love you! Please check out our Gold Customer specials!
21
{else}
22
Become a Gold Customer by buying more stuff here.
23
{/if}
以上比较简单,DemoData.ascx中的JSON内容是直接手打的。下面的DemoData2.ascx.cs中,您将看到如何使用JSONArray和JSONObject类以编程的方式进行JSON序列化。
代码 DemoData2.ascx
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
<%
@ Control Language="C#" AutoEventWireup="true" CodeFile="DemoData2.ascx.cs" Inherits="AjaxTemplates_DemoData2" %>
2
<%= content %>
代码 DemoData2.ascx.cs
1
using System;
2
using System.Data;
3
using System.Configuration;
4
using System.Collections;
5
using System.Web;
6
using System.Web.Security;
7
using System.Web.UI;
8
using System.Web.UI.WebControls;
9
using System.Web.UI.WebControls.WebParts;
10
using System.Web.UI.HtmlControls;
11![](/Images/OutliningIndicators/None.gif)
12
using NBear.Common;
13
using NBear.Common.JSON;
14![](/Images/OutliningIndicators/None.gif)
15
public partial class AjaxTemplates_DemoData2 : NBear.Web.UI.AjaxTemplate
16![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
17
protected string content;
18![](/Images/OutliningIndicators/InBlock.gif)
19
public override void OnAjaxTemplatePreRender(System.Collections.Generic.Dictionary<string, string> callbackParams)
20![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
21
string jstContent = LoadJstContentFromUserControl("../" + callbackParams["jst"]);
22![](/Images/OutliningIndicators/InBlock.gif)
23
JSONObject json = new JSONObject();
24
string jsonStr = new EntitySerializer(EntitySerializeType.Json).SerializeArray(Gateways.TestAccessDb.SelectAll<TestAccessDbAccessEntities.User>());
25
JSONArray users = new JSONArray(jsonStr);
26
json.put("users", users);
27
json.put("jst", jstContent);
28![](/Images/OutliningIndicators/InBlock.gif)
29
content = json.ToString();
30
}
31
}
代码 DemoTemplate2.ascx
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
<%
@ Control Language="C#" AutoEventWireup="true" CodeFile="DemoTemplate2.ascx.cs" Inherits="JsTemplates_DemoTemplate2" %>
2
User(s):
3
<table>
4
<tr><td>ID</td>
5
<td>Name</td>
6
<td>Gender</td>
7
<td>Salary</td>
8
</tr>
9
{for u in users}
10
<tr><td>${u.ID}</td><td>${u.Name}</td>
11
<td>${u.Gender}</td><td>${u.Salary}</td>
12
</tr>
13
{forelse}
14
<tr><td colspan="4">No users!</tr>
15
{/for}
16
</table>
这两组范例都是比较简单的,两种方式都是个定义了一组JSON格式的数据和JST格式的页面模版。
执行页面,并点击按钮,你将看到运行效果。每次执行LoadTemplateData()方法,JSON数据和模版内容都通过AJAX回调返回,在DoBinding1函数中,参数content返回值是一个标准的组合了JST模版和JSON数据的JSON字符串,以var data = eval("(" + content + ")");的方式将JSON数据转换为一个标准的js对象实例。data将包含一个jst属性(在DemoData.ascx.cs和DemoData2.ascx.cs中构造的),包含了JST模版的内容。然后再通过JST类库中的方法进行解析这就生成我们需要的HTML。
小结
以上的范例应该还是比较简单的,我们可以看到,由于解析过程是客户端js执行的,没有服务器负担,并由于JSON序列化的效率更高,序列化后的数据大小远小于XML和HTML,也就减少了数据传输量,同时配合AJAX,整个过程对用户来说是完全透明的,也减少了非AJAX方式页面刷新带来的额外数据传递开销。