Ajax工作原理
Ajax技術核心是XMLHttpRequest物件(簡稱XHR物件),XHR為向伺服器傳送請求和解析伺服器響應提供了流暢的介面。能夠以非同步方式從伺服器獲得更多資訊。意味著使用者不必重新整理頁面也能取得新資料,然後通過DOM將資料插入到頁面中。
XHR物件:
XMLHttpRequest 物件方法如下:
about() 停止當前的請求。
getAllResponseHeaders() 把HTTP請求的所有響應首部作為鍵/值對返回
getResponseHeader("header") 返回指定首部的串值
open("method","URL",[asyncFlag]) :
建立對伺服器的呼叫。method引數可以是GET、POST。url引數可以是相對URL或絕對URL。這個方法還包括可選的引數,是否非同步(true或者false)。
send(content) 向伺服器傳送請求。send接收一個引數,即作為請求主體傳送的資料。如果不需要通過請求主體傳送資料,則必須傳入null,因為這個引數對有些瀏覽器來說是必須的。呼叫send()之後,請求就會分派到伺服器。
setRequestHeader("header", "value"): 把指定首部設定為所提供的值。在設定任何首部之前必須先呼叫open()。設定header並和請求一起傳送 ('post'方法一定要 )
XMLHttpRequest 物件屬性描述:
onreadystatechange:狀態改變的事件觸發器,每個狀態改變時都會觸發這個事件處理器.
readyState: 請求的狀態。
有5個可取值:
0: 未初始化。尚未呼叫open()方法。
1:啟動。已經呼叫open()方法,但尚未呼叫send()方法。
2:傳送。已經呼叫send()方法,但尚未接收到相應。
3:接收。已經接收到部分相應資料。
4:完成。已經接收到全部相應資料,而且已經可以在客戶端使用了。
responseText: 伺服器的響應,返回資料的文字。
responseXML:伺服器的響應,返回資料的相容DOM的XML文件物件 ,這個物件可以解析為一個DOM物件。
status:伺服器的HTTP狀態碼(如:404 = "檔案末找到" 、200 ="成功" ,等等)
statusText: 伺服器返回的狀態文字資訊 ,HTTP狀態碼的相應文字(OK或Not Found(未找到)等等。
XHR建立物件:
所有現代瀏覽器(IE7,firefox,Opera,chrome和safari都支援原生的XMLHttpRequest物件)。
建立XMLHttpRequest物件的語法如下:
xhr = new XMLHttpRequest();
老版本的瀏覽器IE7以下的使用ActiveX物件。
xhr=new ActiveXObject("Microsoft.XMLHTTP");
為此,綜合以上,可以建立跨瀏覽器下的XHR物件,如下所示:
function createXHR(){
var xhr;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xhr=new XMLHttpRequest();
}else{ // code for IE6, IE5
xhr=new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
XMLHttpRequest物件用於和伺服器交換資料實列如下:
<script>
function createXHR(){
var xhr;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xhr=new XMLHttpRequest();
}else{ // code for IE6, IE5
xhr=new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
var xhr = createXHR();
console.log(xhr.readyState);
xhr.onreadystatechange = function(){
console.log(xhr.readyState);
if(xhr.readyState == 4) {
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
console.log(xhr.responseText);
}else {
console.log("request was unsuccessful:"+xhr.status);
}
}
};
//xhr.open('get','ajax.txt',false);
//xhr.send(null);
</script>
一:未呼叫open和send方法之前,xhr.readyState輸出為0,也就是未初始化,未呼叫open方法。
二:當把xhr.open方法開啟時候,xhr.readyState輸出為1,也就是啟動了open方法,但尚未呼叫send()方法。
三:當把xhr.send(null) 程式碼開啟時候,xhr.readyState輸出為4,說明接收到全部資料,且xhr.responseText輸出為ajax.txt的內容了。
請求中使用GET還是POST?
與post相比,get更簡單也更快,並且在大部分情況下可以使用,然後在以下情況下,請使用post請求:
1. 無法使用快取檔案(更新伺服器上的檔案或者資料庫)。
2. 向伺服器傳送大量資料(POST沒有資料量限制)。
3. 傳送包含未知字元的使用者輸入時,post比get更穩定也更可靠。
還是上面的demo,把xhr.open第三個引數改為true,xhr.open('GET','ajax.txt',true); 可以看到列印出來分別為0,1,2,3,4。
使用GET請求會有快取,所以為了避免這種情況,需要在url後面增加唯一的id,比如可以加一個時間戳。
GET請求存在安全性,也就是說安全性不高,且傳輸的資料有限制,所以要傳送大量的資料如上所說,請使用POST請求。
注意:使用GET請求經常會發生一個錯誤,就是查詢字串格式會有問題,查詢字串中每個引數的名稱和值必須使用encodeURIComponent()進行編碼,然後才能放到url的末尾,而且所有名-值對必須由和號(&)分隔,如下面所示:
Xhr.open(“GET”,”ajax.txt?name1=value1&name2=value2”,true);
不過我們可以使用下面的函式可以輔助現有的url的末尾新增查詢字串引數。
function addURLParam(url,name,value) {
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
Demo如下:
<script>
function createXHR(){
var xhr;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xhr=new XMLHttpRequest();
}else{ // code for IE6, IE5
xhr=new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
var xhr = createXHR();
console.log(xhr.readyState);
xhr.onreadystatechange = function(){
console.log(xhr.readyState);
if(xhr.readyState == 4) {
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
console.log(xhr.responseText);
}else {
console.log("request was unsuccessful:"+xhr.status);
}
}
};
function addURLParam(url,name,value) {
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
var url = "ajax.txt";
url = addURLParam(url,"name","Nicholas");
xhr.open('get',url,false);
//xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//xhr.setRequestHeader("MyHeader","MyValue");
xhr.send(null);
</script>
如下所示:
POST請求:
一個簡單的post請求如下:
xhr.open('POST','ajax.txt',true);
xhr.send(null);
如果需要像 HTML 表單那樣 POST 資料,請使用 setRequestHeader() 來新增 HTTP 頭。然後在 send() 方法中規定您希望傳送的資料:如下所示:
xhr.open('POST','ajax.txt',true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send("fname=Bill&lname=Gates");
HTTP頭部資訊:
每個http請求和相應都會帶有相應的頭部資訊,其中有的對開發人員有用,有的沒有什麼用,XHR物件也提供了操作這兩種頭部(即請求頭部和相應頭部)資訊的方法。
預設情況下,在傳送XHR請求的同時,還會傳送下列頭部資訊。
- Accept: 瀏覽器能夠處理的內容型別。
- Accept-Charset:瀏覽器能夠顯示的字符集。
- Accept-Encoding:瀏覽器能夠處理的壓縮編碼。
- Accept-Language:瀏覽器當前設定的語言。
- Connection:瀏覽器與伺服器之間連線的型別。
- Cookie: 當前頁面設定的任何cookie。
- Host: 發出請求的頁面所在的域。
- Referer:發出請求的頁面的URL。
- User-Agent: 瀏覽器的使用者代理的字串。
如上面的demo所示:
跨域-----JSONP
通過XHR實現ajax通訊的一個主要限制,來源於跨域安全策略。預設情況下,XHR物件只能訪問與包含它的頁面同一個埠,同一個協議,同一個域名下的頁面的資原始檔,那麼如果是不同域名下的檔案資源是不允許訪問,這是瀏覽器同源策略(為了安全性考慮)。那麼如果開發中會需要在不同的域名下訪問相對應的資源如何做呢?首先我們想到的是JSONP,那麼什麼是JSONP呢?
我們可以先來看看jsonp.html頁面程式碼如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="jsonp.js"></script>
</head>
<body>
</body>
</html>
其中jsonp.js內容為 alert("測試"); 開啟頁面可以看到彈出對話方塊 測試內容。這是在同域名下訪問的,現在我讓jsonp.js指向不同的域名,看能不能訪問呢?首先在hosts檔案下繫結
127.0.0.1 a.test.com 然後把上面的jsonp.js改成 http://a.test.com/ajax/jsonp.js 接著在 頁面上 訪問 http://localhost/ajax/jsonp1.html 這個連結,也可以看出彈出”測試”文案對話方塊,由此可以知道:<script>標籤的src屬性並不被同源策略所約束,所以可以獲取任何伺服器上指令碼並執行。
什麼是JSONP?
JSONP(JSON With Padding) 是一個非官方的協議,它允許在伺服器端整合Script tags返回至客戶端,通過javascript callback的形式實現跨域訪問。
JSONP的作用?
由於同源策略的限制,XmlHttpRequest只允許請求當前源(域名、協議、埠)的資源,為了實現跨域請求,可以通過script標籤實現跨域請求,然後在服務端輸出JSON資料並執行回撥函式,從而解決了跨域的資料請求。
如何使用JSONP?
JSONP執行過程如下:
首先在客戶端註冊一個callback, 然後把callback的名字傳給伺服器。此時,伺服器先生成 json 資料。 然後以 javascript 語法的方式,生成一個function , function 名字就是傳遞上來的引數 callback名字。最後將 json 資料直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文件,返回給客戶端。如下demo:
首先PHP程式碼如下:
<?php
//服務端返回JSON資料
$arr=array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);
$result=json_encode($arr);
//動態執行回撥函式
$callback=$_GET['callback'];
echo $callback."($result)";
?>
會返回如下內容:Callback名稱({"a":1,"b":2,"c":3,"d":4,"e":5});
JS程式碼如下:
<script type="text/javascript">
function jsonpCallback(result) {
//alert(result);
for(var i in result) {
console.log(i+":"+result[i]);//迴圈輸出a:1,b:2,etc.
}
}
var script = document.createElement("script");
script.type = "text/javascript";
script.src="http://a.test.com/ajax/jsonp.php?callback=jsonpCallback";
document.getElementsByTagName("head")[0].appendChild(script);
</script>
此時傳給伺服器的Callback名稱為jsonpCallback,後臺php返回的格式肯定是:jsonpCallback({"a":1,"b":2,"c":3,"d":4,"e":5}),所以在客戶端jsonpCallback函式能正確的呼叫到。
客戶端JS程式碼在Jquery實現方式1如下:
<script type="text/javascript">
$.getJSON("http://a.test.com/ajax/jsonp.php?callback=?",function(result) {
for(var i in result) {
alert(i+":"+result[i]);//迴圈輸出a:1,b:2,etc.
}
});
</script>
可以正確的輸出。
客戶端JS程式碼在Jquery實現方式2如下:
<script type="text/javascript">
$.ajax({
url:"http://a.test.com/ajax/jsonp.php",
dataType:'jsonp',
data:'',
jsonp:'callback',
success:function(result) {
for(var i in result) {
alert(i+":"+result[i]);//迴圈輸出a:1,b:2,etc.
}
}
});
</script>
比如如下:
我們還可以指定callback名稱,如下: