Vincent.Chan‘s Blog

          常用鏈接

          統(tǒng)計(jì)

          積分與排名

          網(wǎng)站

          最新評(píng)論

          3、面向 Java 開(kāi)發(fā)人員的 Ajax: 構(gòu)建動(dòng)態(tài)的 Java 應(yīng)用程序

          Ajax?為更好的?Web?應(yīng)用程序鋪平了道路

          級(jí)別:?中級(jí)

          Philip?McCarthy?,?軟件開(kāi)發(fā)顧問(wèn),?獨(dú)立咨詢(xún)顧問(wèn)


          2005?年?10?月?20?日

          在?Web?應(yīng)用程序開(kāi)發(fā)中,頁(yè)面重載循環(huán)是最大的一個(gè)使用障礙,對(duì)于?Java™?開(kāi)發(fā)人員來(lái)說(shuō)也是一個(gè)嚴(yán)峻的挑戰(zhàn)。在這個(gè)系列中,作者?Philip?McCarthy?介紹了一種創(chuàng)建動(dòng)態(tài)應(yīng)用程序體驗(yàn)的開(kāi)創(chuàng)性方式。Ajax(異步?javascript?和?XML)是一種編程技術(shù),它允許為基于?Java?的?Web?應(yīng)用程序把?Java?技術(shù)、XML?和?javascript?組合起來(lái),從而打破頁(yè)面重載的范式。
          Ajax(即異步?javascript? 和?XML)是一種?Web?應(yīng)用程序開(kāi)發(fā)的手段,它采用客戶(hù)端腳本與?Web?服務(wù)器交換數(shù)據(jù)。所以,不必采用會(huì)中斷交互的完整頁(yè)面刷新,就可以動(dòng)態(tài)地 更新?Web?頁(yè)面。使用?Ajax,可以創(chuàng)建更加豐富、更加動(dòng)態(tài)的?Web?應(yīng)用程序用戶(hù)界面,其即時(shí)性與可用性甚至能夠接近本機(jī)桌面應(yīng)用程序。

          Ajax?不是一項(xiàng)技術(shù),而更像是一個(gè)?模式?——?一種識(shí)別和描述有用的設(shè)計(jì)技術(shù)的方式。Ajax?是新穎的,因?yàn)樵S多開(kāi)發(fā)人員才剛剛開(kāi)始知道 它,但是所有實(shí)現(xiàn)?Ajax?應(yīng)用程序的組件都已經(jīng)存在若干年了。它目前受到重視是因?yàn)樵?2004?和?2005?年出現(xiàn)了一些基于?Ajax?技術(shù)的 非常棒的動(dòng)態(tài)?Web?UI,最著名的就是?Google?的?GMail?和?Maps?應(yīng)用程序,以及照片共享站點(diǎn)?Flickr。這些用戶(hù)界面具有 足夠的開(kāi)創(chuàng)性,有些開(kāi)發(fā)人員稱(chēng)之為“Web?2.0”,因此對(duì)?Ajax?應(yīng)用程序的興趣飛速上升。

          在這個(gè)系列中,我將提供使用?Ajax?開(kāi)發(fā)應(yīng)用程序需要的全部工具?。在第一篇文章中,我將解釋?Ajax?背后的概念,演示為基于?Java? 的?Web?應(yīng)用程序創(chuàng)建?Ajax?界面的基本步驟。我將使用代碼示例演示讓?Ajax?應(yīng)用程序如此動(dòng)態(tài)的服務(wù)器端?Java?代碼和客戶(hù)端?javascript。最后,我將指出?Ajax?方式的一些不足,以及在創(chuàng)建?Ajax?應(yīng)用程序時(shí)應(yīng)當(dāng)考慮的一些更廣的可用性和訪(fǎng)問(wèn)性問(wèn)題。

          更好的購(gòu)物車(chē)

          可以用?Ajax?增強(qiáng)傳統(tǒng)的?Web?應(yīng)用程序,通過(guò)消除頁(yè)面裝入從而簡(jiǎn)化交互。為了演示這一點(diǎn),我采用一個(gè)簡(jiǎn)單的購(gòu)物車(chē)示例,在向里面添加項(xiàng)目 時(shí),它會(huì)動(dòng)態(tài)更新。這項(xiàng)技術(shù)如果整合到在線(xiàn)商店,那么用戶(hù)可以持續(xù)地瀏覽和向購(gòu)物車(chē)中添加項(xiàng)目,而不必在每次點(diǎn)擊之后都等候完整的頁(yè)面更新。雖然這篇文章 中的有些代碼特定于購(gòu)物車(chē)示例,但是演示的技術(shù)可以應(yīng)用于任何?Ajax?應(yīng)用程序。清單?1?顯示了購(gòu)物車(chē)示例使用的有關(guān)?HTML?代碼,整篇文章中 都會(huì)使用這個(gè)?HTML。


          清單1.?購(gòu)物車(chē)示例的有關(guān)片斷

          <!--?Table?of?products?from?store's?catalog,?one?row?per?item?-->
          <th>Name</th>?<th>Description</th>?<th>Price</th>?<th></th>
          ...
          <tr>
          ??<!--?Item?details?-->
          ??<td>Hat</td>?<td>Stylish?bowler?hat</td>?<td>$19.99</td>
          ??<td>
          ????<!--?Click?button?to?add?item?to?cart?via?Ajax?request?-->
          ????<button?onclick="addToCart('hat001')">Add?to?Cart</button>
          ??</td>
          </tr>
          ...

          <!--?Representation?of?shopping?cart,?updated?asynchronously?-->
          <ul?id="cart-contents">

          ??<!--?List-items?will?be?added?here?for?each?item?in?the?cart?-->
          ??
          </ul>

          <!--?Total?cost?of?items?in?cart?displayed?inside?span?element?-->
          Total?cost:?<span?id="total">$0.00</span>


          Ajax?往返過(guò)程

          Ajax?交互開(kāi)始于叫作?XMLHttpRequest?的?javascript?對(duì)象。顧名思義,它允許客戶(hù)端腳本執(zhí)行 ?HTTP?請(qǐng)求,并解析?XML?服務(wù)器響應(yīng)。Ajax?往返過(guò)程的第一步是創(chuàng)建?XMLHttpRequest?的實(shí)例。在 ?XMLHttpRequest?對(duì)象上設(shè)置請(qǐng)求使用的?HTTP?方法(GET?或?POST)以及目標(biāo)?URL。

          現(xiàn)在,您還記得?Ajax?的第一個(gè)?a?是代表?異步(asynchronous)?嗎?在發(fā)送?HTTP?請(qǐng)求時(shí),不想讓瀏覽器掛著等候服務(wù)器 響應(yīng)。相反,您想讓瀏覽器繼續(xù)對(duì)用戶(hù)與頁(yè)面的交互進(jìn)行響應(yīng),并在服務(wù)器響應(yīng)到達(dá)時(shí)再進(jìn)行處理。為了實(shí)現(xiàn)這個(gè)要求,可以在?XMLHttpRequest? 上注冊(cè)一個(gè)回調(diào)函數(shù),然后異步地分派?XMLHttpRequest。然后控制就會(huì)返回瀏覽器,當(dāng)服務(wù)器響應(yīng)到達(dá)時(shí),會(huì)調(diào)用回調(diào)函數(shù)。

          在?Java?Web?服務(wù)器上,請(qǐng)求同其他?HttpServletRequest?一樣到達(dá)。在解析了請(qǐng)求參數(shù)之后,servlet?調(diào)用必要的應(yīng)用程序邏輯,把響應(yīng)序列化成?XML,并把?XML?寫(xiě)入?HttpServletResponse。

          回到客戶(hù)端時(shí),現(xiàn)在調(diào)用注冊(cè)在?XMLHttpRequest?上的回調(diào)函數(shù),處理服務(wù)器返回的?XML?文檔。最后,根據(jù)服務(wù)器返回的數(shù)據(jù),用?javascript?操縱頁(yè)面的?HTML?DOM,把用戶(hù)界面更新。圖?1?是?Ajax?往返過(guò)程的順序圖。


          圖?1.?Ajax?往返過(guò)程

          現(xiàn)在您對(duì)?Ajax?往返過(guò)程有了一個(gè)高層面的認(rèn)識(shí)。下面我將放大其中的每一步驟,進(jìn)行更詳細(xì)的觀(guān)察。如果過(guò)程中迷了路,請(qǐng)回頭看圖?1?——?由于?Ajax?方式的異步性質(zhì),所以順序并非十分簡(jiǎn)單。


          分派?XMLHttpRequest

          我將從?Ajax?序列的起點(diǎn)開(kāi)始:創(chuàng)建和分派來(lái)自瀏覽器的?XMLHttpRequest。不幸的是,不同的瀏覽器創(chuàng)建?XMLHttpRequest?的方法各不相同。清單?2?的?javascript?函數(shù)消除了這些依賴(lài)于瀏覽器的技巧,它可以檢測(cè)當(dāng)前瀏覽器要使用的正確方式,并返回一個(gè)可以使用的?XMLHttpRequest。最好是把它當(dāng)作輔助代碼:只要把它拷貝到?javascript?庫(kù),并在需要?XMLHttpRequest?的時(shí)候使用它就可以了。


          清單?2.?創(chuàng)建跨瀏覽器的?XMLHttpRequest

          /*
          ?*?Returns?a?new?XMLHttpRequest?object,?or?false?if?this?browser
          ?*?doesn't?support?it
          ?*/
          function?newXMLHttpRequest()?{

          ??var?xmlreq?=?false;

          ??if?(window.XMLHttpRequest)?{

          ????//?Create?XMLHttpRequest?object?in?non-Microsoft?browsers
          ????xmlreq?=?new?XMLHttpRequest();

          ??}?else?if?(window.ActiveXObject)?{

          ????//?Create?XMLHttpRequest?via?MS?ActiveX
          ????try?{
          ??????//?Try?to?create?XMLHttpRequest?in?later?versions
          ??????//?of?Internet?Explorer

          ??????xmlreq?=?new?ActiveXObject("Msxml2.XMLHTTP");

          ????}?catch?(e1)?{

          ??????//?Failed?to?create?required?ActiveXObject

          ??????try?{
          ????????//?Try?version?supported?by?older?versions
          ????????//?of?Internet?Explorer

          ????????xmlreq?=?new?ActiveXObject("Microsoft.XMLHTTP");

          ??????}?catch?(e2)?{

          ????????//?Unable?to?create?an?XMLHttpRequest?with?ActiveX
          ??????}
          ????}
          ??}

          ??return?xmlreq;
          }
          ??
          ?


          稍后我將討論處理那些不支持?XMLHttpRequest?的瀏覽器的技術(shù)。目前,示例假設(shè)清單?2?的?newXMLHttpRequest?函數(shù)總能返回?XMLHttpRequest?實(shí)例。

          返回示例的購(gòu)物車(chē)場(chǎng)景,我想要當(dāng)用戶(hù)在目錄項(xiàng)目上點(diǎn)擊?Add?to?Cart?時(shí)啟動(dòng)?Ajax?交互。名為?addToCart()?的?onclick?處理函數(shù)負(fù)責(zé)通過(guò)?Ajax?調(diào)用來(lái)更新購(gòu)物車(chē)的狀態(tài)(請(qǐng)參閱?清單?1)。正如清單?3?所示,addToCart()?需要做的第一件事是通過(guò)調(diào)用清單?2?的?newXMLHttpRequest()?函數(shù)得到?XMLHttpRequest?對(duì)象。接下來(lái),它注冊(cè)一個(gè)回調(diào)函數(shù),用來(lái)接收服務(wù)器響應(yīng)(我稍后再詳細(xì)解釋這一步;請(qǐng)參閱?清單?6)。

          因?yàn)檎?qǐng)求會(huì)修改服務(wù)器上的狀態(tài),所以我將用?HTTP?POST?做這個(gè)工作。通過(guò)?POST?發(fā)送數(shù)據(jù)要求三個(gè)步驟。第一,需要打開(kāi)與要通信的服 務(wù)器資源的?POST?連接?——?在這個(gè)示例中,服務(wù)器資源是一個(gè)映射到?URL?cart.do?的?servlet。然后,我在 ?XMLHttpRequest?上設(shè)置一個(gè)頭,指明請(qǐng)求的內(nèi)容是表單?編碼的數(shù)據(jù)。最后,我用表單編碼的數(shù)據(jù)作為請(qǐng)求體發(fā)送請(qǐng)求。

          清單?3?把這些步驟放在了一起。


          清單?3.?分派?Add?to?Cart?XMLHttpRequest

          /*
          ?*?Adds?an?item,?identified?by?its?product?code,?to?the?shopping?cart
          ?*?itemCode?-?product?code?of?the?item?to?add.
          ?*/
          function?addToCart(itemCode)?{

          ??//?Obtain?an?XMLHttpRequest?instance
          ??var?req?=?newXMLHttpRequest();

          ??//?Set?the?handler?function?to?receive?callback?notifications
          ??//?from?the?request?object
          ??var?handlerFunction?=?getReadyStateHandler(req,?updateCart);
          ??req.onreadystatechange?=?handlerFunction;
          ??
          ??//?Open?an?HTTP?POST?connection?to?the?shopping?cart?servlet.
          ??//?Third?parameter?specifies?request?is?asynchronous.
          ??req.open("POST",?"cart.do",?true);

          ??//?Specify?that?the?body?of?the?request?contains?form?data
          ??req.setRequestHeader("Content-Type",?
          ???????????????????????"application/x-www-form-urlencoded");

          ??//?Send?form?encoded?data?stating?that?I?want?to?add?the?
          ??//?specified?item?to?the?cart.
          ??req.send("action=add&item="+itemCode);
          }


          這就是建立?Ajax?往返過(guò)程的第一部分,即創(chuàng)建和分派來(lái)自客戶(hù)機(jī)的?HTTP?請(qǐng)求。接下來(lái)是用來(lái)處理請(qǐng)求的?Java?servlet?代碼。


          servlet?請(qǐng)求處理

          用?servlet?處理?XMLHttpRequest,與處理普通的瀏覽器?HTTP?請(qǐng)求一樣。可以用 ?HttpServletRequest.getParameter()?得到在?POST?請(qǐng)求體中發(fā)送的表單編碼數(shù)據(jù)。Ajax?請(qǐng)求被放進(jìn)與來(lái)自應(yīng) 用程序的常規(guī)?Web?請(qǐng)求一樣的?HttpSession?中。對(duì)于示例購(gòu)物車(chē)場(chǎng)景來(lái)說(shuō),這很有用,因?yàn)檫@讓我可以把購(gòu)物車(chē)狀態(tài)封裝在 ?JavaBean?中,并在請(qǐng)求之間在會(huì)話(huà)中維持這個(gè)狀態(tài)。

          清單?4?是處理?Ajax?請(qǐng)求、更新購(gòu)物車(chē)的簡(jiǎn)單?servlet?的一部分。Cart?bean?是從用戶(hù)會(huì)話(huà)中獲得的,并根據(jù)請(qǐng)求參數(shù)更新 它的狀態(tài)。然后?Cart?被序列化成?XML,XML?又被寫(xiě)入?ServletResponse。重要的是把響應(yīng)的內(nèi)容類(lèi)型設(shè)置為 ?application/xml,否則?XMLHttpRequest?不會(huì)把響應(yīng)內(nèi)容解析成?XML?DOM。


          清單?4.?處理?Ajax?請(qǐng)求的?servlet?代碼

          public?void?doPost(HttpServletRequest?req,?HttpServletResponse?res)
          ????????????????????????throws?java.io.IOException?{

          ??Cart?cart?=?getCartFromSession(req);

          ??String?action?=?req.getParameter("action");
          ??String?item?=?req.getParameter("item");
          ??
          ??if?((action?!=?null)&&(item?!=?null))?{

          ????//?Add?or?remove?items?from?the?Cart
          ????if?("add".equals(action))?{
          ??????cart.addItem(item);

          ????}?else?if?("remove".equals(action))?{
          ??????cart.removeItems(item);

          ????}
          ??}

          ??//?Serialize?the?Cart's?state?to?XML
          ??String?cartXml?=?cart.toXml();

          ??//?Write?XML?to?response.
          ??res.setContentType("application/xml");
          ??res.getWriter().write(cartXml);
          }

          ?


          清單?5?顯示了?Cart.toXml()?方法生成的示例?XML。它很簡(jiǎn)單。請(qǐng)注意?cart?元素的?generated?屬性,它是?System.currentTimeMillis()?生成的一個(gè)時(shí)間戳。


          清單?5.?Cart?對(duì)象的XML?序列化示例?

          <?xml?version="1.0"?>
          <cart?generated="1123969988414"?total="$171.95">
          ??<item?code="hat001">
          ????<name>Hat</name>
          ????<quantity>2</quantity>
          ??</item>
          ??<item?code="cha001">
          ????<name>Chair</name>
          ????<quantity>1</quantity>
          ??</item>
          ??<item?code="dog001">
          ????<name>Dog</name>
          ????<quantity>1</quantity>
          ??</item>
          </cart>

          ?


          如果查看應(yīng)用程序源代碼(可以從?下載?一節(jié)得到)中的?Cart.java,可以看到生成?XML?的方式只是把字符串添加在一起。雖然對(duì)這個(gè)示例來(lái)說(shuō)足夠了,但是對(duì)于從?Java?代碼生成?XML?來(lái)說(shuō)則是最差的方式。我將在這個(gè)系列的下一期中介紹一些更好的方式。

          現(xiàn)在您已經(jīng)知道了?CartServlet?響應(yīng)?XMLHttpRequest?的方式。下一件事就是返回客戶(hù)端,查看如何用?XML?響應(yīng)更新頁(yè)面狀態(tài)。


          用?javascript?進(jìn)行響應(yīng)處理

          XMLHttpRequest?的?readyState?屬性是一個(gè)數(shù)值,它指出請(qǐng)求生命周期的狀態(tài)。它從?0(代表“未初始化”)變化到?4 (代表“完成”)。每次?readyState?變化時(shí),readystatechange?事件就觸發(fā),由?onreadystatechange?屬 性指定的事件處理函數(shù)就被調(diào)用。

          在?清單?3?中已經(jīng)看到了如何調(diào)用?getReadyStateHandler()?函數(shù)創(chuàng)建事件處理函數(shù)。然后把這個(gè)事件處理函數(shù)分配給?onreadystatechange?屬性。getReadyStateHandler()?利用了這樣一個(gè)事實(shí):函數(shù)是?javascript? 中的一級(jí)對(duì)象。這意味著函數(shù)可以是其他函數(shù)的參數(shù),也可以創(chuàng)建和返回其他函數(shù)。getReadyStateHandler()?的工作是返回一個(gè)函數(shù),檢 查?XMLHttpRequest?是否已經(jīng)完成,并把?XML?響應(yīng)傳遞給調(diào)用者指定的事件處理函數(shù)。清單?6?是 ?getReadyStateHandler()?的代碼。


          清單?6.?getReadyStateHandler()?函數(shù)

          /*
          ?*?Returns?a?function?that?waits?for?the?specified?XMLHttpRequest
          ?*?to?complete,?then?passes?its?XML?response?to?the?given?handler?function.
          ?*?req?-?The?XMLHttpRequest?whose?state?is?changing
          ?*?responseXmlHandler?-?Function?to?pass?the?XML?response?to
          ?*/
          function?getReadyStateHandler(req,?responseXmlHandler)?{

          ??//?Return?an?anonymous?function?that?listens?to?the?
          ??//?XMLHttpRequest?instance
          ??return?function?()?{

          ????//?If?the?request's?status?is?"complete"
          ????if?(req.readyState?==?4)?{
          ??????
          ??????//?Check?that?a?successful?server?response?was?received
          ??????if?(req.status?==?200)?{

          ????????//?Pass?the?XML?payload?of?the?response?to?the?
          ????????//?handler?function
          ????????responseXmlHandler(req.responseXML);

          ??????}?else?{

          ????????//?An?HTTP?problem?has?occurred
          ????????alert("HTTP?error:?"+req.status);
          ??????}
          ????}
          ??}
          }

          ?

          按此在新窗口瀏覽圖片?HTTP?狀態(tài)碼

          在清單?6?中,檢查?XMLHttpRequest?的?status?屬性以查看請(qǐng)求是否成功完成。status?包含服務(wù)器響應(yīng)的 ?HTTP?狀態(tài)碼。在執(zhí)行簡(jiǎn)單的?GET?和?POST?請(qǐng)求時(shí),可以假設(shè)任何大于?200?(OK)的碼都是錯(cuò)誤。如果服務(wù)器發(fā)送重定向響應(yīng)(例如 ?301?或?302),瀏覽器會(huì)透明地進(jìn)行重定向并從新的位置獲取資源;XMLHttpRequest?看不到重定向狀態(tài)碼。而且,瀏覽器會(huì)自動(dòng)添加 ?Cache-Control:?no-cache?頭到所有?XMLHttpRequest,這樣客戶(hù)代碼永遠(yuǎn)也不用處理?304(未經(jīng)修改)服務(wù)器響 應(yīng)。
          ?
          ?
          關(guān)于?getReadyStateHandler()

          getReadyStateHandler()?是段相對(duì)復(fù)雜的代碼,特別是如果您不習(xí)慣閱讀?javascript?的話(huà)。但是通過(guò)把這個(gè)函數(shù)放在?javascript?庫(kù)中,就可以處理?Ajax?服務(wù)器響應(yīng),而不必處理?XMLHttpRequest?的內(nèi)部細(xì)節(jié)。重要的是要理解如何在自己的代碼中使用?getReadyStateHandler()。

          在?清單?3? 中看到了?getReadyStateHandler()?像這樣被調(diào)用:handlerFunction?= ?getReadyStateHandler(req,?updateCart)。在這個(gè)示例中,getReadyStateHandler()?返回的 函數(shù)將檢查在?req?變量中的?XMLHttpRequest?是否已經(jīng)完成,然后用響應(yīng)的?XML?調(diào)用名為?updateCart?的函數(shù)。

          提取購(gòu)物車(chē)數(shù)據(jù)

          清單?7?是?updateCart()?本身的代碼。函數(shù)用?DOM?調(diào)用檢查購(gòu)物車(chē)的?XML?文檔,然后更新?Web?頁(yè)面(請(qǐng)參閱?清單?1), 反映新的購(gòu)物車(chē)內(nèi)容。這里的重點(diǎn)是用來(lái)從?XML?DOM?提取數(shù)據(jù)的調(diào)用。cart?元素的?generated?屬性是在?Cart?序列化為 ?XML?時(shí)生成的一個(gè)時(shí)間戳,檢查它可以保證新的購(gòu)物車(chē)數(shù)據(jù)不會(huì)被舊的數(shù)據(jù)覆蓋。Ajax?請(qǐng)求天生是異步的,所以這個(gè)檢查可以處理服務(wù)器響應(yīng)未按次序 到達(dá)的情況。


          清單?7.?更新頁(yè)面,反映購(gòu)物車(chē)的?XML?文檔

          function?updateCart(cartXML)?{

          ?//?Get?the?root?"cart"?element?from?the?document
          ?var?cart?=?cartXML.getElementsByTagName("cart")[0];

          ?//?Check?that?a?more?recent?cart?document?hasn't?been?processed
          ?//?already
          ?var?generated?=?cart.getAttribute("generated");
          ?if?(generated?>?lastCartUpdate)?{
          ???lastCartUpdate?=?generated;

          ???//?Clear?the?HTML?list?used?to?display?the?cart?contents
          ???var?contents?=?document.getElementById("cart-contents");
          ???contents.innerHTML?=?"";

          ???//?Loop?over?the?items?in?the?cart
          ???var?items?=?cart.getElementsByTagName("item");
          ???for?(var?I?=?0?;?I?<?items.length?;?I++)?{

          ?????var?item?=?items[I];

          ?????//?Extract?the?text?nodes?from?the?name?and?quantity?elements
          ?????var?name?=?item.getElementsByTagName("name")[0]
          ???????????????????????????????????????????????.firstChild.nodevalue;
          ???????????????????????????????????????????????
          ?????var?quantity?=?item.getElementsByTagName("quantity")[0]
          ???????????????????????????????????????????????.firstChild.nodevalue;

          ?????//?Create?and?add?a?list?item?HTML?element?for?this?cart?item
          ?????var?li?=?document.createElement("li");
          ?????li.appendChild(document.createTextNode(name+"?x?"+quantity));
          ?????contents.appendChild(li);
          ???}
          ?}

          ?//?Update?the?cart's?total?using?the?value?from?the?cart?document
          ?document.getElementById("total").innerHTML?=?
          ??????????????????????????????????????????cart.getAttribute("total");
          }

          ?


          到此,整個(gè)?Ajax?往返過(guò)程完成了,但是您可能想讓?Web?應(yīng)用程序運(yùn)行一下查看實(shí)際效果(請(qǐng)參閱?下載?一節(jié))。這個(gè)示例非常簡(jiǎn)單,有很多需要改進(jìn)之處。例如,我包含了從購(gòu)物車(chē)中清除項(xiàng)目的服務(wù)器端代碼,但是無(wú)法從?UI?訪(fǎng)問(wèn)它。作為一個(gè)好的練習(xí),請(qǐng)?jiān)囍趹?yīng)用程序現(xiàn)有的?javascript?代碼之上構(gòu)建出能夠?qū)崿F(xiàn)這個(gè)功能的代碼。


          使用?Ajax?的挑戰(zhàn)

          就像任何技術(shù)一樣,使用?Ajax?也有許多出錯(cuò)的可能性。我目前在這里討論的問(wèn)題還缺乏容易的解決方案,但是會(huì)隨著?Ajax?的成熟而改進(jìn)。隨著開(kāi)發(fā)人員社區(qū)增加開(kāi)發(fā)?Ajax?應(yīng)用程序的經(jīng)驗(yàn),將會(huì)記錄下最佳實(shí)踐和指南。

          XMLHttpRequest?的可用性

          Ajax?開(kāi)發(fā)人員面臨的一個(gè)最大問(wèn)題是:在沒(méi)有?XMLHttpRequest?可用時(shí)該如何響應(yīng)?雖然主要的現(xiàn)代瀏覽器都支持 ?XMLHttpRequest,但仍然有少數(shù)用戶(hù)的瀏覽器不支持,或者瀏覽器的安全設(shè)置阻止使用?XMLHttpRequest。如果開(kāi)發(fā)的?Web? 應(yīng)用程序要部署在企業(yè)內(nèi)部網(wǎng),那么可能擁有指定支持哪種瀏覽器的權(quán)力,從而可以認(rèn)為?XMLHttpRequest?總能使用。但是,如果要部署在公共 ?Web?上,那么就必須當(dāng)心,如果假設(shè)?XMLHttpRequest?可用,那么就可能會(huì)阻止那些使用舊的瀏覽器、殘疾人專(zhuān)用瀏覽器和手持設(shè)備上的輕 量級(jí)瀏覽器的用戶(hù)使用您的應(yīng)用程序。

          所以,您應(yīng)當(dāng)努力讓?xiě)?yīng)用程序“平穩(wěn)降級(jí)”,在沒(méi)有?XMLHttpRequest?支持的瀏覽器中也能夠工作。在購(gòu)物車(chē)的示例中,把應(yīng)用程序降級(jí)的 最好方式可能是讓?Add?to?Cart?按鈕執(zhí)行一個(gè)常規(guī)的表單提交,刷新頁(yè)面來(lái)反映購(gòu)物車(chē)更新后的狀態(tài)。Ajax?的行為應(yīng)當(dāng)在頁(yè)面裝入的時(shí)候就通 過(guò)?javascript?添加到頁(yè)面,只有在?XMLHttpRequest?可用時(shí)才把?javascript?事件處理函數(shù)附加到每個(gè)?Add?to?Cart?按鈕。另一種方式是在用戶(hù)登錄時(shí)檢測(cè)?XMLHttpRequest?是否可用,然后相應(yīng)地提供應(yīng)用程序的?Ajax?版本或基于表單的普通版本。

          可用性考慮

          關(guān)于?Ajax?應(yīng)用程序的某些可用性問(wèn)題比較普遍。例如,讓用戶(hù)知道他們的輸入已經(jīng)注冊(cè)了可能是重要的,因?yàn)樯陈┕鈽?biāo)和?spinning?瀏覽 器的常用反饋機(jī)制“throbber”對(duì)?XMLHttpRequest?不適用。一種技術(shù)是用“Now?updating...”類(lèi)型的信息替換 ?Submit?按鈕,這樣用戶(hù)在等候響應(yīng)期間就不會(huì)反復(fù)單擊按鈕了。

          另一個(gè)問(wèn)題是,用戶(hù)可能沒(méi)有注意到他們正在查看的頁(yè)面的某一部分已經(jīng)更新了??梢允褂貌煌目梢暭夹g(shù),把用戶(hù)的眼球帶到頁(yè)面的更新區(qū)域,從而緩解這 個(gè)問(wèn)題。由?Ajax?更新頁(yè)面造成的其他問(wèn)題還包括:“破壞了”瀏覽器的后退按鈕,地址欄中的?URL?也無(wú)法反映頁(yè)面的整個(gè)狀態(tài),妨礙了設(shè)置書(shū)簽。請(qǐng) 參閱?參考資料?一節(jié),獲得專(zhuān)門(mén)解決?Ajax?應(yīng)用程序可用性問(wèn)題的文章。

          服務(wù)器負(fù)載

          用?Ajax?實(shí)現(xiàn)代替普通的基于表單的?UI,會(huì)大大提高對(duì)服務(wù)器發(fā)出的請(qǐng)求數(shù)量。例如,一個(gè)普通的?Google?Web?搜索對(duì)服務(wù)器只有一 個(gè)請(qǐng)求,是在用戶(hù)提交搜索表單時(shí)出現(xiàn)的。而?Google?Suggest?試圖自動(dòng)完成搜索術(shù)語(yǔ),它要在用戶(hù)輸入時(shí)向服務(wù)器發(fā)送多個(gè)請(qǐng)求。在開(kāi)發(fā) ?Ajax?應(yīng)用程序時(shí),要注意將要發(fā)送給服務(wù)器的請(qǐng)求數(shù)量以及由此造成的服務(wù)器負(fù)荷。降低服務(wù)器負(fù)載的辦法是,在客戶(hù)機(jī)上對(duì)請(qǐng)求進(jìn)行緩沖并且緩存服務(wù)器 響應(yīng)(如果可能的話(huà))。還應(yīng)該嘗試將?Ajax?Web?應(yīng)用程序設(shè)計(jì)為在客戶(hù)機(jī)上執(zhí)行盡可能多的邏輯,而不必聯(lián)絡(luò)服務(wù)器。

          處理異步

          非常重要的是,要理解無(wú)法保證?XMLHttpRequest?會(huì)按照分派它們的順序完成。實(shí)際上,應(yīng)當(dāng)假設(shè)它們不會(huì)按順序完成,并且在設(shè)計(jì)應(yīng)用程序時(shí)把這一點(diǎn)記在心上。在購(gòu)物車(chē)的示例中,使用最后更新的時(shí)間戳來(lái)確保新的購(gòu)物車(chē)數(shù)據(jù)不會(huì)被舊的數(shù)據(jù)覆蓋(請(qǐng)參閱?清單?7)。這個(gè)非常基本的方式可以用于購(gòu)物車(chē)場(chǎng)景,但是可能不適合其他場(chǎng)景。所以在設(shè)計(jì)時(shí)請(qǐng)考慮如何處理異步的服務(wù)器響應(yīng)。


          結(jié)束語(yǔ)

          現(xiàn)在您對(duì)?Ajax?的基本原則應(yīng)當(dāng)有了很好的理解,對(duì)參與?Ajax?交互的客戶(hù)端和服務(wù)器端組件也應(yīng)當(dāng)有了初步的知識(shí)。這些是基于?Java? 的?Ajax?Web?應(yīng)用程序的構(gòu)造塊。另外,您應(yīng)當(dāng)理解了伴隨?Ajax?方式的一些高級(jí)設(shè)計(jì)問(wèn)題。創(chuàng)建成功的?Ajax?應(yīng)用程序要求整體考慮,從 ?UI?設(shè)計(jì)到?javascript?設(shè)計(jì),再到服務(wù)器端架構(gòu);但是您現(xiàn)在應(yīng)當(dāng)已經(jīng)武裝了考慮其他這些方面所需要的核心?Ajax?知識(shí)。

          如果使用這里演示的技術(shù)編寫(xiě)大型?Ajax?應(yīng)用程序的復(fù)雜性讓您覺(jué)得恐慌,那么有好消息給您。由于?Struts、Spring?和 ?Hibernate?這類(lèi)框架的發(fā)展把?Web?應(yīng)用程序開(kāi)發(fā)從底層?Servlet?API?和?JDBC?的細(xì)節(jié)中抽象出來(lái),所以正在出現(xiàn)簡(jiǎn)化 ?Ajax?開(kāi)發(fā)的工具包。其中有些只側(cè)重于客戶(hù)端,提供了向頁(yè)面添加可視效果的簡(jiǎn)便方式,或者簡(jiǎn)化了對(duì)?XMLHttpRequest?的使用。有些則 走得更遠(yuǎn),提供了從服務(wù)器端代碼自動(dòng)生成?Ajax?接口的方式。這些框架替您完成了繁重的任務(wù),所以您可以采用更高級(jí)的方式進(jìn)行?Ajax?開(kāi)發(fā)。我在 這個(gè)系列中將研究其中的一些。

          Ajax?社區(qū)正在快速前進(jìn),所以會(huì)有大量有價(jià)值的信息涌現(xiàn)。在閱讀這個(gè)系列的下一期之前,我建議您參考?參考資料?一節(jié)中列出的文章,特別是如果您是剛接觸?Ajax?或客戶(hù)端開(kāi)發(fā)的話(huà)。您還應(yīng)當(dāng)花些時(shí)間研究示例源代碼并考慮一些增強(qiáng)它的方式。

          在這個(gè)系列的下一篇文章中,我將深入討論?XMLHttpRequest?API,并推薦一些從?JavaBean?方便地創(chuàng)建?XML?的方式。我還將介紹替代?XML?進(jìn)行?Ajax?數(shù)據(jù)傳遞的方式,例如?JSON(javascript?Object?Notation)輕量級(jí)數(shù)據(jù)交換格式。

          下載

          描述?名字?大小??下載方法?
          Sample?code?j-ajax1.zip?8?KB??FTP?
          按此在新窗口瀏覽圖片?
          按此在新窗口瀏覽圖片?關(guān)于下載方法的信息?按此在新窗口瀏覽圖片?按此在新窗口瀏覽圖片?獲取?Adobe??Reader??

          posted on 2006-02-19 10:51 草兒 閱讀(4) 評(píng)論(0) ?編輯?收藏收藏至365K

          posted on 2006-03-18 18:58 Vincent.Chen 閱讀(194) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): AJAX

          主站蜘蛛池模板: 东丰县| 古蔺县| 武城县| 祁连县| 湘潭市| 太原市| 资兴市| 安塞县| 肥城市| 田林县| 西乡县| 舟曲县| 临潭县| 武义县| 大同县| 吉木乃县| 吴堡县| 新化县| 宜春市| 大余县| 深水埗区| 大渡口区| 乡城县| 江山市| 肇庆市| 南皮县| 阆中市| 南陵县| 东方市| 朝阳区| 太康县| 南开区| 武鸣县| 阿图什市| 浦城县| 临邑县| 南澳县| 长泰县| 南阳市| 吴忠市| 桑日县|