您的位置:首页 > 其它

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)和
XMLHTTPRequest
对象。DHTML由三个元素组合而成,它们分别是超文本标记语言(HypertextMarkupLanguage,HTML)、JavaScript代码和级联样式表(CascadingStyleSheet,CSS)。在Web页面使用JavaScript代码,可以动态地改变页面,包括添加、删除或更改页面内容。这就是DHTML的动态部分。JavaScript代码使用
XMLHTTPRequest
对象在加载页面后向服务器请求数据。

这两种元素的组合——从服务器动态请求数据然后使用这些数据更改页面——就是Ajax的本质,也是Web2.0站点的动态特性。

但这并没有真正告诉您如何实际应用这些特性以及如何在站点中使用它们。因此,需要一组简单的设计模式。如果您对这个术语感到陌生,本文推介了一本非常优秀的同名书籍(参见参考资料)。这本书针对工程师经常面对的任务提供了一组实现模式。它不仅提供了设计系统的最佳实践,还介绍了工程师谈论代码时用到的术语。

本文介绍了五种常见Ajax设计模式。它们在使用HTML、XML和JavaScript代码从服务器获取数据方面有所不同。我先介绍最简单的模式,它将使用来自服务器的新HTML页面来更新页面。

模式1.替换HTML片段

最常见的Ajax任务也许就是向服务器请求更新的HTML并使用它更新部分页面。可能需要周期性地完成这一任务——比如,更新股市报价。也可能要按需更新——比如,对搜索请求进行响应。

清单1中的代码从服务器请求一个页面然后将内容放入页面主体的
<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展示了代码请求的内容。

清单2.Pat1_content.html


HTMLencodedcontentgoeshere.



在Firefox中加载页面后,可以看到图1所示的结果。

图1.替换了<div>标记的页面



现在回到清单1中的代码,来观察一些内容。第一个要注意的是
loadUrl()
函数,它从服务器请求一个URL。该函数使用
XMLHTTPRequest
对象向服务器请求新内容。它还指定了一个回调函数——本例中,是
processReqChange
——当浏览器收到内容时将调用它。

processReqChange
函数将查看对象以确定请求是否完成。如果是的话,该函数将页面
<div>
标记的
innerHTML
设置为响应的文本。

<div>
标记作为一个动态内容的占位符,这是Ajax代码的主要组成部分。这些标记没有可见的表示形式(除非添加边框,像我这样做),但它们很好地标记了内容的放置位置。工程师还使用
<span>
标记用于可代替的片段,稍后我将对其进行演示。
<div>
<span>
标记的不同之处是前者加入了一个断行符(如一个段落),而后者使用边线勾画出一节内联文本。

暂时回到
processReqChange
函数,该函数对
status
readyState
的值进行检查非常重要。有些浏览器可能只在请求完成时才调用这个函数,而也有些浏览器会不断回调该函数从而告诉代码请求依然在运行。

选项卡式显示变体

该模式的另一种变体就是创建一个选项卡样式的显示。清单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.



这些代码演示的是
<span>
标记的用法,而非
<div>
标记。所使用的方法取决于用户界面(UI)的需求。但是正如您所看到的那样,无论哪种方法使用起来都很简单。

为页面获取新的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显示页面



此页面和上一个模式中显示的页面之间最大的区别就是
processReqChange
函数,这里没有使用
responseText
,而是
responseXML
,这是一个XML文档对象模型(DocumentObjectModel,DOM),该模型只有在来自服务器的响应是正确编码的XML时才是可用的。

通过使用
responseXML
,我请求了XML文档的
<book>
标记的列表。然后分别从中获取
<title>
<author>
元素。接下来,为每本书向表中添加一行,再为每行添加包含作者和题目数据的单元格。

这是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');


varbooks=eval(req.responseText);




for(varbinbooks)...{


varelTr=dtable.insertRow(-1);




varelAuthorTd=elTr.insertCell(-1);


elAuthorTd.innerHTML=books.author;




varelTitleTd=elTr.insertCell(-1);


elTitleTd.innerHTML=books[b].title;


}}}




...



所有的HTML代码保持不变。
processReqChange
函数仅仅更改为读取
eval
从而从服务器返回JavaScript数据。该函数随后使用
eval
的JavaScript对象作为数据源,后者又将其添加到表中。

清单11显示了来自服务器的JavaScript数据。

[b]清单11.Pat3_js_data.js





[...{author:'JackHerrington',title:'CodeGenerationinAction'},




...{author:'JackHerrington',title:'PodcastingHacks'},




...{author:'JackHerrington',title:'PHPHacks'}


]



为什么众多Ajax应用程序工程师更喜欢使用JavaScript代码而不是XML来对数据编码?答案很明显。JavaScript代码更容易读取和管理,并且也更容易被浏览器处理。

收集和显示所有的数据之后,即可看到Ajax的重点就是显示当前数据——当前最重要的部分。那么,如何保证总是能够从服务器获得最新的数据呢?




模式4.避免浏览器缓存

浏览器会尝试化Web流量,所以如果您对同一个URL请求两次,很可能还不如重新请求一次页面,您的浏览器将仅仅使用浏览器缓存中存储的页面。所以,Ajax应用程序中另一个常见模式是使用URL中的随机元素来保证浏览器不会返回一个缓存的结果。

我最喜欢的技巧就是向URL添加当前时间的数字值。清单12展示了这一技巧。

清单12.Pat4_cache.html


<html>




<script>...


...






functionloadUrl(url)...{


url=url+"?t="+((newDate()).valueOf());


...


}




...



代码取自清单1,对URL字符串执行了一些额外的JavaScript文本操作。我将URL连接到一个新的参数
t
,它具有一个时间值。服务器是否能识别这个值实际上并不重要。这只不过是用来确保浏览器忽视其基于URL的页面缓存。




模式5.替换多个HTML片段

最后要演示的这个模式是第一个模式的高级版本:包含来自服务器内容的
<div>
标记的替代物。Web应用程序中的一个常见问题是在响应用户输入时,必须更新显示页面中的一些区域。举例来说,在一个股票报价应用程序中,显示页面的一部分可能会展示最近的报价,而另一部分则显示最近报价的列表。

为更新显示页面中的多个区域,我使用了服务器的XML响应,它包含每个部分的数据。然后,使用一个正则表达式将响应分解为单个部分。清单13显示了这种技巧。

清单13.Pat5_multi_segment.html


<html>


<head>




<script>...


varreq=null;




functionprocessReqChange()...{




if(req.readyState==4&&req.status==200)...{


varone=req.responseText.match(/<one>(.*?)</one>/);


document.getElementById('divOne').innerHTML=one[1];


vartwo=req.responseText.match(/<two>(.*?)</two>/);


document.getElementById('divTwo').innerHTML=two[1];


}}






functionloadXMLDoc(url)...{...}




varurl=window.location.toString();


url=url.replace(/pat5_multi_segment.html/,'pat5_data.xml');


loadXMLDoc(url);


</script>


</head>


<body>




Thisisthecontentforsegmentone:<br/>


<divid="divOne"style="border:1pxsolidblack;padding:10px;">


</div>


Andsegmenttwo:<br/>


<divid="divTwo"style="border:1pxsolidblack;padding:10px;">


</div>




</body>


</html>



清单14展示了来自服务器的数据。

清单14.Pat5_data.xml


<segments>


<one>Contentforsegmentone</one>


<two>Contentforsegment<b>two</b></two>


</segments>



在浏览器中加载这段代码时,将看到图7所示的结果。

图7.使用服务器的数据更新显示页面中的两部分



在页面代码中,我还可以使用XML响应,因为服务器返回的是有效的XML。但是使用正则表达式比从XML代码中分解单独部分更加简单。

结束语

Ajax的功能之强大与之被误解和误用的程度相当。本文中演示的模式为在Web应用程序中使用Ajax提供了一个不错的起点。除了使用这里提供的代码,我还建议您关注以下Web2.0革命带来的某些出色的Ajax和WebUI库。其中最主要的是Prototype.js库,它提供了向浏览器发送以及从浏览器获取数据的简便方法,以及浏览器间兼容的方法更新Web页面内容。使用这些库的价值在于工程师可以在广泛的浏览器和平台上专注地维护和测试它们,这样可省去大量的工作和麻烦。

无论从哪方面说,就像本文中的模式演示的那样,Ajax可以为您的应用程序添加动态行为。

comefrom:http://www.ibm.com/developerworks/cn/xml/x-ajaxxml2/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: