Ajax 和 XML: 五种常见 Ajax 模式
2007-03-24 12:09
609 查看
Ajax和XML:五种常见Ajax模式
AsynchronousJavaScript+XML(Ajax)无疑是2006年最热门的技术术语,且有望在2007得到进一步发展。但是对您的应用程序来说它究竟有什么意义呢?Ajax应用程序中哪一种常见架构模式应用最广泛呢?本文将介绍五种常见Ajax设计模式,可以使用它们作为工作的基础。
的确,Ajax是Web2.0热门术语,所有人都希望将其应用于自己的站点。但是它对我们究竟有什么意义?工程师该如何在架构的层面上将其集成到自己的站点中?在这篇文章中,我将介绍Ajax的基本知识,并展示一些已经成为Web2.0开发最佳实践的Ajax设计模式。
首先,Ajax仅仅是一个涉及一组技术的术语,包括DynamicHTML(DHTML)和
这两种元素的组合——从服务器动态请求数据然后使用这些数据更改页面——就是Ajax的本质,也是Web2.0站点的动态特性。
但这并没有真正告诉您如何实际应用这些特性以及如何在站点中使用它们。因此,需要一组简单的设计模式。如果您对这个术语感到陌生,本文推介了一本非常优秀的同名书籍(参见参考资料)。这本书针对工程师经常面对的任务提供了一组实现模式。它不仅提供了设计系统的最佳实践,还介绍了工程师谈论代码时用到的术语。
本文介绍了五种常见Ajax设计模式。它们在使用HTML、XML和JavaScript代码从服务器获取数据方面有所不同。我先介绍最简单的模式,它将使用来自服务器的新HTML页面来更新页面。
模式1.替换HTML片段
最常见的Ajax任务也许就是向服务器请求更新的HTML并使用它更新部分页面。可能需要周期性地完成这一任务——比如,更新股市报价。也可能要按需更新——比如,对搜索请求进行响应。
清单1中的代码从服务器请求一个页面然后将内容放入页面主体的
清单1.Pat1_replace_div.html
<html>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardobj=document.getElementById('htmlDiv');
dobj.innerHTML=req.responseText;
}
}
functionloadUrl(url)...{
if(window.XMLHttpRequest)...{
try...{req=newXMLHttpRequest();
}catch(e)...{req=false;}
}elseif(window.ActiveXObject)...{
try...{req=newActiveXObject('Msxml2.XMLHTTP');
}catch(e)...{
try...{req=newActiveXObject('Microsoft.XMLHTTP');
}catch(e)...{req=false;}
}}
if(req)...{
req.onreadystatechange=processReqChange;
req.open('GET',url,true);
req.send('');
}
}
varurl=window.location.toString();
url=url.replace(/pat1_replace_div.html/,'pat1_content.html');
loadUrl(url);
</script>
<body>
Dynamiccontentisshownbetweenhere:<br/>
<divid="htmlDiv"style="border:1pxsolidblack;padding:10px;">
</div>
Andhere.<br/>
</body>
</html>
清单2展示了代码请求的内容。
清单2.Pat1_content.html
HTMLencodedcontentgoeshere.
在Firefox中加载页面后,可以看到图1所示的结果。
图1.替换了<div>标记的页面
现在回到清单1中的代码,来观察一些内容。第一个要注意的是
将
暂时回到
选项卡式显示变体
该模式的另一种变体就是创建一个选项卡样式的显示。清单3展示了一个简单的选项卡式Ajax界面。
清单3.Pat1_tabs.html
<html>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardobj=document.getElementById('tabDiv');
dobj.innerHTML=req.responseText;
}
}
functionloadUrl(tab)...{
varurl=window.location.toString();
url=url.replace(/pat1_tabs.html/,tab);
...
}
functiontab1()...{loadUrl('pat1_tab1_content.html');}
functiontab2()...{loadUrl('pat1_tab2_content.html');}
tab1();
</script>
<body>
<ahref="javascript:voidtab1();">Tab1<a>
<ahref="javascript:voidtab2();">Tab2<a>
<divid="tabDiv"style="border:1pxsolidblack;padding:10px;">
</div>
</body>
</html>
清单4显示了第一个选项卡的内容。
清单4.Pat1_tab1_content.html
清单5显示了第二个选项卡的内容。
清单5.Pat1_tab2_content.html
Tab2content
当我在自己的浏览器上显示该页面时,我看到了第一个选项卡,如图2所示。
图2.第一个选项卡的内容
然后单击第二个选项卡的链接。浏览器检索第二个选项卡的内容然后将它显示在选项卡区域,如图3所示。
图3.第二个选项卡的内容
这是该设计模式的最典型用法——从用户那里获得请求并使用新的内容更新部分显示,本例演示了创建选项卡显示的技巧。应用程序端的价值就是您可以为用户下载非常轻量级的页面,用户可以根据自己的需求访问这些内容。
在Ajax出现之前,最常见的技术是将所有的选项卡都放在页面上,然后根据需要显示或隐藏它们。这就是说即使从来不查看第二个选项卡,也会为其创建HTML,既浪费服务器时间又浪费带宽。使用这种新的Ajax方法,只有当用户请求第二个选项卡时才会为其创建HTML。
readmore变体
该模式的另一个变化就是Readmore链接,如图4所示。
图4.我的博客登录处的Readmore链接
假如想希望阅读更多关于我遛狗的经历,可以单击Readmore链接,使该链接替换为完整的故事,如图5所示。
图5.单击Readmore链接后显示的页面
这样做的好处是顾客可以在无需刷新页面的情况下获得更多内容。
清单6显示了该页的代码。
清单6.Pat1_readmore.html
<html>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardobj=document.getElementById("moreSpan");
dobj.innerHTML=req.responseText;
}
}
functionloadUrl(url)...{...}
functiongetMore()
...{
varurl=window.location.toString();
url=url.replace(/pat1_readmore.html/,'pat1_readmore_content.html');
loadUrl(url);
}
</script>
<body>
<h1>Walkingthedog</h1>
Itookmydogforawalktoday.
<spanid="moreSpan">
<ahref="javascript:voidgetMore()">Readmore...</a>
</span>
</body>
</html>
清单7显示了“readmore”部分的内容。
清单7.Pat1_readmore_content.html
Itwasanicedayout.Warmandsunny.Mydoglikedgettingoutforastretch.
这些代码演示的是
为页面获取新的HTML只是其中一件事情,如果您希望JavaScript代码在页面中使用数据执行一些更智能化的任务该怎么办呢?如何使用结构化的方式将数据发送到浏览器呢?毫无疑问,这正是使用XML的原因。
模式2.读取XML数据
出于某些原因,Ajax已成为XML的同义词,尽管XML不是绝对必要的。从上面几个例子可以看出,您完全可以返回简单的文本甚至是HTML片段——或者ExtensibleHTML(XHTML)——代码。但是发送XML自有其优势所在。
清单8显示的Ajax代码首先向服务器请求图书记录,然后将数据显示在页面内的表格中。
清单8.Pat2_xml.html
<html>
<head>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200&&req.responseXML)...{
vardtable=document.getElementById('dataBody');
varnl=req.responseXML.getElementsByTagName('book');
for(vari=0;i<nl.length;i++)...{
varnli=nl.item(i);
varelAuthor=nli.getElementsByTagName('author');
varauthor=elAuthor.item(0).firstChild.nodeValue;
varelTitle=nli.getElementsByTagName('title');
vartitle=elTitle.item(0).firstChild.nodeValue;
varelTr=dtable.insertRow(-1);
varelAuthorTd=elTr.insertCell(-1);
elAuthorTd.innerHTML=author;
varelTitleTd=elTr.insertCell(-1);
elTitleTd.innerHTML=title;
}}}
functionloadXMLDoc(url)...{
if(window.XMLHttpRequest)...{
try...{req=newXMLHttpRequest();
}catch(e)...{req=false;}
}elseif(window.ActiveXObject)...{
try...{req=newActiveXObject('Msxml2.XMLHTTP');
}catch(e)...{
try...{req=newActiveXObject('Microsoft.XMLHTTP');
}catch(e)...{req=false;}
}}
if(req)...{
req.onreadystatechange=processReqChange;
req.open('GET',url,true);
req.send('');
}
}
varurl=window.location.toString();
url=url.replace(/pat2_xml.html/,'pat2_xml_data.xml');
loadXMLDoc(url);
</script>
</head>
<body>
<tablecellspacing="0"cellpadding="3"width="100%">
<tbodyid="dataBody">
<tr>
<thwidth="20%">Author</th>
<thwidth="80%">Title</th>
</tr>
</tbody>
</table>
</body>
</html>
清单9显示了该页面的数据。
清单9.Pat2_xml_data.xml
<books>
<book>
<author>JackHerrington</author>
<title>CodeGenerationinAction</title>
</book>
<book>
<author>JackHerrington</author>
<title>PodcastingHacks</title>
</book>
<book>
<author>JackHerrington</author>
<title>PHPHacks</title>
</book>
</books>
在浏览器中加载页面时,我看到了如图6所示的结果。
图6.XML显示页面
此页面和上一个模式中显示的页面之间最大的区别就是
通过使用
这是XML数据的最基本应用。更复杂的JavaScript代码可以执行客户端排序或根据返回的数据进行搜索。
遗憾的是,传递XML数据的缺点是需要浏览器多花费一些时间来解析整个XML文档。同样,JavaScript代码在XML中查找数据也很复杂(参见清单8)。一个替代办法是从服务器请求JavaScript代码。
模式3.读取JavaScript数据
从服务器请求JavaScript数据这种技术通常用于JavaScriptObjectNotation(JSON)这种良好的代码。返回JavaScript数据的优点就是能够使浏览器高效地解析并创建使用起来更加简单的JavaScript数据结构。
让我们将清单8中从服务器读取XML的代码修改为从服务器读取JavaScript数据的代码。新代码如清单10所示。
清单10.Pat3_js.html
<html><head><script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardtable=document.getElementById('dataBody');
AsynchronousJavaScript+XML(Ajax)无疑是2006年最热门的技术术语,且有望在2007得到进一步发展。但是对您的应用程序来说它究竟有什么意义呢?Ajax应用程序中哪一种常见架构模式应用最广泛呢?本文将介绍五种常见Ajax设计模式,可以使用它们作为工作的基础。
的确,Ajax是Web2.0热门术语,所有人都希望将其应用于自己的站点。但是它对我们究竟有什么意义?工程师该如何在架构的层面上将其集成到自己的站点中?在这篇文章中,我将介绍Ajax的基本知识,并展示一些已经成为Web2.0开发最佳实践的Ajax设计模式。
首先,Ajax仅仅是一个涉及一组技术的术语,包括DynamicHTML(DHTML)和
XMLHTTPRequest对象。DHTML由三个元素组合而成,它们分别是超文本标记语言(HypertextMarkupLanguage,HTML)、JavaScript代码和级联样式表(CascadingStyleSheet,CSS)。在Web页面使用JavaScript代码,可以动态地改变页面,包括添加、删除或更改页面内容。这就是DHTML的动态部分。JavaScript代码使用
XMLHTTPRequest对象在加载页面后向服务器请求数据。
这两种元素的组合——从服务器动态请求数据然后使用这些数据更改页面——就是Ajax的本质,也是Web2.0站点的动态特性。
但这并没有真正告诉您如何实际应用这些特性以及如何在站点中使用它们。因此,需要一组简单的设计模式。如果您对这个术语感到陌生,本文推介了一本非常优秀的同名书籍(参见
本文介绍了五种常见Ajax设计模式。它们在使用HTML、XML和JavaScript代码从服务器获取数据方面有所不同。我先介绍最简单的模式,它将使用来自服务器的新HTML页面来更新页面。
模式1.替换HTML片段
最常见的Ajax任务也许就是向服务器请求更新的HTML并使用它更新部分页面。可能需要周期性地完成这一任务——比如,更新股市报价。也可能要按需更新——比如,对搜索请求进行响应。
<div>标记中。
清单1.Pat1_replace_div.html
<html>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardobj=document.getElementById('htmlDiv');
dobj.innerHTML=req.responseText;
}
}
functionloadUrl(url)...{
if(window.XMLHttpRequest)...{
try...{req=newXMLHttpRequest();
}catch(e)...{req=false;}
}elseif(window.ActiveXObject)...{
try...{req=newActiveXObject('Msxml2.XMLHTTP');
}catch(e)...{
try...{req=newActiveXObject('Microsoft.XMLHTTP');
}catch(e)...{req=false;}
}}
if(req)...{
req.onreadystatechange=processReqChange;
req.open('GET',url,true);
req.send('');
}
}
varurl=window.location.toString();
url=url.replace(/pat1_replace_div.html/,'pat1_content.html');
loadUrl(url);
</script>
<body>
Dynamiccontentisshownbetweenhere:<br/>
<divid="htmlDiv"style="border:1pxsolidblack;padding:10px;">
</div>
Andhere.<br/>
</body>
</html>
清单2.Pat1_content.html
HTMLencodedcontentgoeshere.
在Firefox中加载页面后,可以看到
图1.替换了<div>标记的页面
现在回到
loadUrl()函数,它从服务器请求一个URL。该函数使用
XMLHTTPRequest对象向服务器请求新内容。它还指定了一个回调函数——本例中,是
processReqChange——当浏览器收到内容时将调用它。
processReqChange函数将查看对象以确定请求是否完成。如果是的话,该函数将页面
<div>标记的
innerHTML设置为响应的文本。
将
<div>标记作为一个动态内容的占位符,这是Ajax代码的主要组成部分。这些标记没有可见的表示形式(除非添加边框,像我这样做),但它们很好地标记了内容的放置位置。工程师还使用
<span>标记用于可代替的片段,稍后我将对其进行演示。
<div>和
<span>标记的不同之处是前者加入了一个断行符(如一个段落),而后者使用边线勾画出一节内联文本。
暂时回到
processReqChange函数,该函数对
status和
readyState的值进行检查非常重要。有些浏览器可能只在请求完成时才调用这个函数,而也有些浏览器会不断回调该函数从而告诉代码请求依然在运行。
选项卡式显示变体
该模式的另一种变体就是创建一个选项卡样式的显示。
清单3.Pat1_tabs.html
<html>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardobj=document.getElementById('tabDiv');
dobj.innerHTML=req.responseText;
}
}
functionloadUrl(tab)...{
varurl=window.location.toString();
url=url.replace(/pat1_tabs.html/,tab);
...
}
functiontab1()...{loadUrl('pat1_tab1_content.html');}
functiontab2()...{loadUrl('pat1_tab2_content.html');}
tab1();
</script>
<body>
<ahref="javascript:voidtab1();">Tab1<a>
<ahref="javascript:voidtab2();">Tab2<a>
<divid="tabDiv"style="border:1pxsolidblack;padding:10px;">
</div>
</body>
</html>
清单4.Pat1_tab1_content.html
清单5.Pat1_tab2_content.html
Tab2content
当我在自己的浏览器上显示该页面时,我看到了第一个选项卡,如
图2.第一个选项卡的内容
然后单击第二个选项卡的链接。浏览器检索第二个选项卡的内容然后将它显示在选项卡区域,如
图3.第二个选项卡的内容
这是该设计模式的最典型用法——从用户那里获得请求并使用新的内容更新部分显示,本例演示了创建选项卡显示的技巧。应用程序端的价值就是您可以为用户下载非常轻量级的页面,用户可以根据自己的需求访问这些内容。
在Ajax出现之前,最常见的技术是将所有的选项卡都放在页面上,然后根据需要显示或隐藏它们。这就是说即使从来不查看第二个选项卡,也会为其创建HTML,既浪费服务器时间又浪费带宽。使用这种新的Ajax方法,只有当用户请求第二个选项卡时才会为其创建HTML。
readmore变体
该模式的另一个变化就是Readmore链接,如
图4.我的博客登录处的Readmore链接
假如想希望阅读更多关于我遛狗的经历,可以单击Readmore链接,使该链接替换为完整的故事,如
图5.单击Readmore链接后显示的页面
这样做的好处是顾客可以在无需刷新页面的情况下获得更多内容。
清单6.Pat1_readmore.html
<html>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardobj=document.getElementById("moreSpan");
dobj.innerHTML=req.responseText;
}
}
functionloadUrl(url)...{...}
functiongetMore()
...{
varurl=window.location.toString();
url=url.replace(/pat1_readmore.html/,'pat1_readmore_content.html');
loadUrl(url);
}
</script>
<body>
<h1>Walkingthedog</h1>
Itookmydogforawalktoday.
<spanid="moreSpan">
<ahref="javascript:voidgetMore()">Readmore...</a>
</span>
</body>
</html>
清单7.Pat1_readmore_content.html
Itwasanicedayout.Warmandsunny.Mydoglikedgettingoutforastretch.
这些代码演示的是
<span>标记的用法,而非
<div>标记。所使用的方法取决于用户界面(UI)的需求。但是正如您所看到的那样,无论哪种方法使用起来都很简单。
为页面获取新的HTML只是其中一件事情,如果您希望JavaScript代码在页面中使用数据执行一些更智能化的任务该怎么办呢?如何使用结构化的方式将数据发送到浏览器呢?毫无疑问,这正是使用XML的原因。
出于某些原因,Ajax已成为XML的同义词,尽管XML不是绝对必要的。从上面几个例子可以看出,您完全可以返回简单的文本甚至是HTML片段——或者ExtensibleHTML(XHTML)——代码。但是发送XML自有其优势所在。
清单8.Pat2_xml.html
<html>
<head>
<script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200&&req.responseXML)...{
vardtable=document.getElementById('dataBody');
varnl=req.responseXML.getElementsByTagName('book');
for(vari=0;i<nl.length;i++)...{
varnli=nl.item(i);
varelAuthor=nli.getElementsByTagName('author');
varauthor=elAuthor.item(0).firstChild.nodeValue;
varelTitle=nli.getElementsByTagName('title');
vartitle=elTitle.item(0).firstChild.nodeValue;
varelTr=dtable.insertRow(-1);
varelAuthorTd=elTr.insertCell(-1);
elAuthorTd.innerHTML=author;
varelTitleTd=elTr.insertCell(-1);
elTitleTd.innerHTML=title;
}}}
functionloadXMLDoc(url)...{
if(window.XMLHttpRequest)...{
try...{req=newXMLHttpRequest();
}catch(e)...{req=false;}
}elseif(window.ActiveXObject)...{
try...{req=newActiveXObject('Msxml2.XMLHTTP');
}catch(e)...{
try...{req=newActiveXObject('Microsoft.XMLHTTP');
}catch(e)...{req=false;}
}}
if(req)...{
req.onreadystatechange=processReqChange;
req.open('GET',url,true);
req.send('');
}
}
varurl=window.location.toString();
url=url.replace(/pat2_xml.html/,'pat2_xml_data.xml');
loadXMLDoc(url);
</script>
</head>
<body>
<tablecellspacing="0"cellpadding="3"width="100%">
<tbodyid="dataBody">
<tr>
<thwidth="20%">Author</th>
<thwidth="80%">Title</th>
</tr>
</tbody>
</table>
</body>
</html>
清单9.Pat2_xml_data.xml
<books>
<book>
<author>JackHerrington</author>
<title>CodeGenerationinAction</title>
</book>
<book>
<author>JackHerrington</author>
<title>PodcastingHacks</title>
</book>
<book>
<author>JackHerrington</author>
<title>PHPHacks</title>
</book>
</books>
在浏览器中加载页面时,我看到了如
图6.XML显示页面
此页面和上一个模式中显示的页面之间最大的区别就是
processReqChange函数,这里没有使用
responseText,而是
responseXML,这是一个XML文档对象模型(DocumentObjectModel,DOM),该模型只有在来自服务器的响应是正确编码的XML时才是可用的。
通过使用
responseXML,我请求了XML文档的
<book>标记的列表。然后分别从中获取
<title>和
<author>元素。接下来,为每本书向表中添加一行,再为每行添加包含作者和题目数据的单元格。
这是XML数据的最基本应用。更复杂的JavaScript代码可以执行客户端排序或根据返回的数据进行搜索。
遗憾的是,传递XML数据的缺点是需要浏览器多花费一些时间来解析整个XML文档。同样,JavaScript代码在XML中查找数据也很复杂(参见
从服务器请求JavaScript数据这种技术通常用于JavaScriptObjectNotation(JSON)这种良好的代码。返回JavaScript数据的优点就是能够使浏览器高效地解析并创建使用起来更加简单的JavaScript数据结构。
让我们将
清单10.Pat3_js.html
<html><head><script>...
varreq=null;
functionprocessReqChange()...{
if(req.readyState==4&&req.status==200)...{
vardtable=document.getElementById('dataBody');