讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 第7章 數據交互和存儲 >

第7章 數據交互和存儲

Ajax是高性能JavaScript的基石,是提升網站可用性的最大改進之一。利用Ajax可以在網站大量使用異步請求,解決需要加載太多資源的問題。Ajax可以通過延遲下載大量資源使頁面加載更快,還可以通過在客戶端和服務器之間異步傳送數據避免頁面集體加載。Ajax還用於在一次HTTP請求中獲取整個頁面的資源,通過選擇正確的傳輸技術和最有效的數據格式,可以顯著改善用戶與網站之間的互動。

作為數據格式,純文本和HTML是被高度限制的,但它們可節省客戶端的CPU週期。XML被廣泛應用和普遍支持,但它非常冗長且解析緩慢。JSON是輕量級的,解析迅速,交互性與XML相當。字符分隔的自定義格式是非常輕量級的,在大量數據集解析時速度最快,但需要編寫額外的程序在服務器端構造格式,並且需要在客戶端解析。

XHR(XMLHttpRequest)將所有傳入數據視為一個字符串,這種用法方便控制,讓使用更具靈活性,當然它也有缺點,可能會降低解析速度。另外,XHR雖然允許跨域請求和本地運行,但是這個接口不夠安全,且不能讀取信息頭或響應報文代碼。大部分XHR可減少請求的數量,可在一次響應中處理不同的文件類型,儘管它不能緩存收到的響應報文。XHR也可用POST方法發送大量數據。

建議137:使用隱藏框架實現異步通信

隱藏框架只是異步交互的一個載體,它僅負責信息的傳輸,而交互的核心是一種信息處理機制,這種處理機制就是回調函數。所謂回調函數,就是客戶端頁面中的一個普通函數,不過該函數在服務器端被調用,並負責處理服務器端響應的信息。

下面示例初步展現了異步信息交互中請求和響應的完整過程,其中回調函數的處理是整個流程的焦點。

第1步,構建一個框架集(回調處理.htm),如下:


<html>

<head>

<title>使用隱藏框架與服務器進行異步通信</title>

</head>

<frameset rows=\"*,0\">

<frame src=\"回調處理_main.htm\"name=\"main\"/>

<frame src=\"回調處理_black.htm\"name=\"serve\"/>

</frameset>

<noframes>你的瀏覽器不支持框架集,請升級瀏覽器版本!</noframes>

</html>


這個框架集由上下兩個框架組成,框架2(下框架)高度為0。建議盡量不要設置高為0像素,因為在一些老版本的瀏覽器中依然會顯示高度為0的框架。這兩個框架的分工如下:

❑框架1(main),負責與用戶進行信息交互。

❑框架2(serve),負責與服務器進行信息交互。

由於老版本瀏覽器可能不支持框架技術,所以應使用<noframes>來兼容它們,使設計顯得更友好。

第2步,在默認狀態下,框架集中的框架2加載一個空白頁面(回調處理_black.htm),框架1加載與客戶進行交互的頁面(回調處理_main.htm)。

框架1中主要包含兩個函數:一個是響應用戶操作的回調函數,另一個是向服務器發送請求的事件處理函數。具體代碼如下:


<html>

<head>

<title>與客戶交互頁面</title>

<script language=\"javascript\"type=\"text/javascript\">

function request{//向服務器發送請求的異步請求函數

var user=document.getElementById(\"user\");//獲取輸入的用戶名

var pass=document.getElementById(\"pass\");//獲取輸入密碼

var s=\"user=\"+user.value+\"&pass=\"+pass.value;//構造查詢字符串

parent.frames[1].location.href=\"回調處理_serve.htm?\"+s;/*為框架集中的框架2加載服務器端請求文件,並附加查詢字符串,傳送客戶端信息,以實現異步信息的雙向交互*/

}

function callback(b,n){//異步交互的回調函數

if(b){//如果參數b為真,說明輸入信息正確

var e=document.getElementsByTagName(\"body\")[0];/*獲取框架1中body元素的引用指針,以實現向其中插入信息*/

e.innerHTML=\"<h1>\"+n+\"</h1><p>您好,歡迎登錄站點</p>\";/*在交互頁面中插入新的交互信息*/

}

else{//如果參數b為假,說明輸入信息不正確

alert(\"你輸入的用戶名或密碼有誤,請重新輸入\");//提示重新輸入信息

var user=parent.frames[0].document.getElementById(\"user\");/*獲取框架1中的用戶名文本框*/

var pass=parent.frames[0].document.getElementById(\"pass\");/*獲取框架1中的密碼文本框*/

user.;

//清空用戶名文本框中的值

pass.;

//清空密碼文本框中的值

}}

window.onload=function{//頁面初始化處理函數

var b=document.getElementById(\"submit\");//獲取【提交】按鈕b.onclick=request;//綁定鼠標單擊事件處理函數

}

</script>

</head><body>

<h1>用戶登錄</h1>

用戶名:<input name=\"\"id=\"user\"type=\"text\"><br/>

密 碼:<input name=\"\"id=\"pass\"type=\"password\"><br/>

<input name=\"submit\"type=\"button\"id=\"submit\"/></body>

</html>


由於回調函數是在服務器端文件中被調用的,因此對像作用域的範圍就發生了變化,此時應該指明它的框架集和框架名或序號,否則在頁面操作中會找不到指定的元素。

第3步,在服務器端的文件中設計響應處理函數,該函數將分解HTTP傳遞過來的URL信息,獲取查詢字符串,並且根據查詢字符串中用戶名和密碼來判斷當前輸入的信息是否正確,進而決定具體響應的信息。


<html><head>

<title>服務器端響應和處理頁面</title>

<script language=\"javascript\"type=\"text/javascript\">window.onload=function{

//服務器響應處理函數,當該頁面被請求加載時觸發

var query=location.search.substring(1);//獲取HTTP請求的URL中所包含的查詢字符串var a=query.split(\"&\");

//將查詢字符串分離成數組

var o={};

//臨時對像直接量

for(var i=0;i<a.length;i++){

//遍歷查詢字符串數組

var pos=a[i].indexOf(\"=\");

//找到等號的下標位置

if(pos==-1)continue;

//如果沒有等號,則忽略

var name=a[i].substring(0,pos);

//獲取等號前面的字符串

var value=a[i].substring(pos+1);

//獲取等號後面的字符串

o[name]=unescape(value);

//把名-值對傳遞給對像

}

var n,b;

((o[\"user\"])&&o[\"user\"]==\"admin\")?(n=o[\"user\"]):(n=null);

/*如果用戶

名存在,且等於\"admin\",則記錄該信息,否則設置為null*/

((o[\"pass\"])&&o[\"pass\"]==\"123456\")?(b=true):(b=false);/*如果密碼

存在,且等於\"123456\",則設置變量b為true,否則為false*/

parent.frames[0].callback(b,n);/*調用客戶端框架集中框架1中的回調函數,並把處理的信

息傳遞給它*/

}

</script>

</head><body>

<h1>服務器端響應和處理頁面</h1>

</body></html>


在實際開發中,服務器端文件一般為動態服務器類型的文件,並且借助服務器端腳本來獲取用戶的信息,然後決定響應的內容,一般還會像查詢數據庫一樣返回查詢內容等。本示例以簡化的形式演示這個異步通信的過程,因此沒有採用服務器技術。預覽框架集,在客戶交互頁面中輸入用戶的登錄信息,在向服務器提交請求之後,服務器首先接收從客戶端傳遞過來的信息並進行處理,然後調用客戶端的回調函數把處理後的信息響應回去。