Ajax 全稱是異步的 JavaScript 和 XML 。 經過在後臺與服務器進行少許數據交換,AJAX 可使網頁實現異步更新。這意味着能夠在不從新加載整個網頁的狀況下,對網頁的某部分進行更新。傳統的網頁(不使用 AJAX)若是須要更新內容,必須重載整個網頁頁面。javascript
Ajax 具備如下優勢和缺點:php
同源策略是Netscape提出的一個著名的安全策略,它是指同一個「源頭」的數據能夠自由訪問,但不一樣源的數據相互之間都不能訪問。咱們試想一下如下幾種狀況:html
很明顯,第1個和第3個例子中,不一樣的天貓商店和 qq 空間屬於同源,能夠共享登陸信息。qq 爲了區別不一樣的 qq 的登陸信息,從新打開了一個窗口,由於瀏覽器的不一樣窗口是不能共享信息的。而第2個例子中的支付寶、網銀、不知名網站之間是非同源的,因此彼此之間沒法訪問信息,若是你執意想請求數據,會提示異常:前端
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
那麼什麼是同源的請求呢?同源請求要求被請求資源頁面和發出請求頁面知足3個相同:java
協議相同
域名相同
端口相同
簡單理解一下:ajax
/*如下兩個數據非同源,由於協議不一樣*/ http://www.abc123.com.cn/item/a.js https://www.abc123.com.cn/item/a.js /*如下兩個數據非同源,由於域名不一樣*/ http://www.abc123.com.cn/item/a.js http://www.abc123.com/item/a.js /*如下兩個數據非同源,由於主機名不一樣*/ http://www.abc123.com.cn/item/a.js http://item.abc123.com.cn/item/a.js /*如下兩個數據非同源,由於協議不一樣*/ http://www.abc123.com.cn/item/a.js http://www.abc123.com.cn:8080/item/a.js /* 如下兩個數據非同源,域名和 ip 視爲不一樣源 * 這裏應注意,ip和域名替換同樣不是同源的 * 假設www.abc123.com.cn解析後的 ip 是 195.155.200.134 */ http://www.abc123.com.cn/ http://195.155.200.134/ /*如下兩個數據同源*/ /* 這個是同源的*/ http://www.abc123.com.cn/source/a.html http://www.abc123.com.cn/item/b.js
Ajax在編寫時一共4個步驟:chrome
常見的發送方式有 GET 和 POST,除此以外還有 HEAD, DELETE, TRACE, PUT, CONNECT, OPTIONS和 PATCH等,這裏只舉例前兩個 GET 和 POST。json
例如根據姓名查詢一我的的信息並寫在div#output中segmentfault
//GET 方法 function search(name, fun){ var xhr = new XMLHttpRequest(); var url = "search.php?name=" + window.encodeURIComponent(name) + "&t=" + Math.random(); xhr.open("GET", url); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4 && xhr.status == 200){ var data = JSON.parse(xhr.responseText); //獲取了 JSON 字符串 fun(data); } } } function show(data){ this.innerHTML = "姓名:" + data.name + "<br />性別:" + data.gender + "<br />年齡:" + data.age + "<br />地址:" + data.address + "<br />電話:" + data.tel; } var output = document.getElementById("output"); search("李華", show.bind(output)); //服務器端 search.php <?php $name = $_GET[name]; //模擬數據查詢結果 echo '{"name":"' . $name .'","age":18,"gender":"男","tel":"13211112222","address":"北京市海淀區xxxxxxxx"}'; ?>
//POST方法 function search(name, fun){ var xhr = new XMLHttpRequest(); var url = "search.php"; var para = "name=" + window.encodeURIComponent(name) + "&t=" + Math.random(); xhr.open("POST", url); //POST方式下,必須把 Content-Type 設置爲application/x-www-form-urlencoded xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onreadystatechange = function(){ if(xhr.readyState == 4 && xhr.status == 200){ console.log(xhr.responseText); var data = JSON.parse(xhr.responseText); //獲取了 JSON 字符串 fun(data); } } xhr.send(para); } function show(data){ this.innerHTML = "姓名:" + data.name + "<br />性別:" + data.gender + "<br />年齡:" + data.age + "<br />地址:" + data.address + "<br />電話:" + data.tel; } var output = document.getElementById("output"); search("李華", show.bind(output)); //服務器端 search.php <?php $name = $_POST[name]; //模擬數據查詢結果 echo '{"name":"' . $name .'","age":18,"gender":"男","tel":"13211112222","address":"北京市海淀區xxxxxxxx'; ?>
上述代碼的 jQuery 寫法:跨域
//GET 方式 function search(name, fun){ var url = "search.php?name=" + window.encodeURIComponent(name) + "&t=" + Math.random(); $.get(url, fun); } function show(data){ data = JSON.parse(data); this.innerHTML = "姓名:" + data.name + "<br />性別:" + data.gender + "<br />年齡:" + data.age + "<br />地址:" + data.address + "<br />電話:" + data.tel; } var output = document.getElementById("output"); search("李華", show.bind(output));
//POST 方式 function search(name, fun){ var url = "search.php"; var obj = {}; obj.name = name; obj.t = Math.random(); $.post(url, obj, fun); } function show(data){ data = JSON.parse(data); this.innerHTML = "姓名:" + data.name + "<br />性別:" + data.gender + "<br />年齡:" + data.age + "<br />地址:" + data.address + "<br />電話:" + data.tel; } var output = document.getElementById("output"); search("李華", show.bind(output));
細心一些能夠發現,上面發送請求的數據中加入了一個隨機數 t。由於有時服務器更新的了數據後,咱們再一次執行 Ajax 請求不能顯示新的結果,這是因爲 js 爲了加速,頁面會使用緩存保持當前調用的相同連接。咱們加了一個隨機數之後,每次請求不一樣,瀏覽器就不會使用緩存數據了。
返回的中文數據亂碼是由於 js 頁面和action頁面中使用了不一樣的編碼方式致使的。能夠有如下2中方式解決(瀏覽器 html 文件是 urf-8 編碼的):
tips: 考慮到兼容性,第1個方法更好
以前的代碼並無按兼容性的格式書寫,不過 Ajax 的兼容也不難,主要表如今 XMLHTTPRequest對象獲取環節:
var xhr; if(XMLHttpRequest){ xhr = new XMLHttpRequest(); //chrome, safari, opera, firefox } else if(ActionXObject){ try{ xhr = new ActionXObject("Msxml2.XMLHTTP"); //IE 中 Msxml 插件 }catch(e){ xhr = new ActionXObject("Microsoft.XMLHTTP"); //IE } }
--- | GET | POST |
---|---|---|
後退/刷新 | 無害 | 數據會從新提交 |
書籤 | 可藏爲書籤 | 沒法藏爲書籤 |
緩存 | 能夠緩存 | 不能夠緩存 |
MIME類型 | application/x-www-from-urlencode | application/x-www-from-urlencode或 multipart/form-data (二進制爲多重編碼 |
歷史記錄 | 參數保留在歷史記錄中 | 參數不會留在歷史記錄 |
數據長度 | URL最長2048個字符(2kB) | 無限 |
數據類型 | ASCII字符 | 無限 |
安全性 | 差 | 較 |
可見性 | 數據可見 | 數據不可見 |
這裏須要強調的是,jsonp不屬於Ajax的部分,它只是吧url放入script標籤中實現的數據傳輸,主要優勢是不受同源策略限制。因爲通常庫也會把它和Ajax封裝在一塊兒,因此這裏放在一塊兒討論。下面是一個jsonp的例子(實現功能:輸入手機號碼查詢歸屬地和運營商):
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>兼容問題</title> </head> <body> <form> <input type="text" name="tel" id="tel" /> <input type="button" value="search" id="search"/> <br/> </form> <div id="output"></div> </body> <script> function jsonpCallback(data) { document.getElementById('output').innerHTML = data.province + " " + data.catName; } document.getElementById('search').onclick = function(){ var num = document.getElementById('tel').value; if(/^1[34578]\d{9}$/.test(num)){ var url = "http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=" + num + "t=" + Math.random() + "&callback=jsonpCallback"; var JSONP=document.createElement("script"); JSONP.type="text/javascript"; JSONP.src= url; document.getElementsByTagName("head")[0].appendChild(JSONP); } else { alert("您輸入的手機號有誤") } }; </script> </html>
上述代碼的所有js部分能夠用jQuery實現,以下:
function jsonpCallback(data) { $('#output').text(data.province + " " + data.catName); } $('#search').click(function(){ var num = $('#tel').val(); if(/^1[34578]\d{9}$/.test(num)){ var url = "http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=" + num" + "t=" + Math.random(); $.ajax({ url: url, type: 'GET', dataType: 'JSONP', // 處理Ajax跨域問題(本質已不是Ajax) success: function(data){ $('#output').text(data.province + " " + data.catName); } }); } else { alert("您輸入的手機號有誤") } });
//屬性 xhr.responseText; //從服務器返回的字符串數據 xhr.responseXML; //從服務器返回的 XML 數據 xhr.status; //服務器相應狀態 xhr.readyState; //0: 請求未初始化; 1: 已創建鏈接; 2: 請求已接收; 3: 請求處理中; 4: 響應已就緒 xhr.timeout; //指定多少毫秒後超時,長整型 xhr.upload; //獲取上傳進度 xhr.withCredentials; //是否能夠跨源,boolean 型,默認 false //方法 xhr.getResponseHeader('connection'); //獲取指定頭信息 xhr.getAllResponseHeaders(); //獲所有定頭信息 xhr.open("METHOD", url, isAsyn); //open方法有3個參數,最後一個參數是 Boolean 型,表示是否異步,默認爲 true xhr.abort(); //終止請求,置xhr.readyState爲0,但不觸發onreadystatechange xhr.overrideMimeType() //強制重寫 http 頭的 MIME 類型 //事件 XMLHttpRequestEventTarget.onreadystatechange //在xhr.readyState屬性改變時觸發 XMLHttpRequestEventTarget.ontimeout //在響應超時時觸發 XMLHttpRequestEventTarget.onabort //當請求失敗時調用該方法 XMLHttpRequestEventTarget.onerror //當請求發生錯誤時調用該方法 XMLHttpRequestEventTarget.onload //當一個HTTP請求正確加載出內容後返回時調用。 XMLHttpRequestEventTarget.onloadstart //當一個HTTP請求開始加載數據時調用。 XMLHttpRequestEventTarget.onloadend //當內容加載完成,無論失敗與否,都會調用該方法 XMLHttpRequestEventTarget.onprogress //間歇調用該方法用來獲取請求過程當中的信息。
注:關於 xhr.status 可能的返回值,詳見 http狀態碼
$.ajax({options}) //發起一個 ajax 請求 options 經常使用如下屬性設置:url, method("GET"/"POST"), crossDomain, accepts(可接受的類型), dataType, cache, contentType(編碼格式), success, error等 $.ajaxSetup({options}); //options同上,設置 ajax 默認參數,不建議使用 $.post(url, data, success, datatype); //發起一個 POST 請求 data爲傳遞參數(可選), success(reponseText, statusText, xhr) 爲成功時的回調函數(可選), datatype(xml/html/script/json/jsonp/text,可選) $.get(url, data, success, datatype); //發起一個 GET 請求, 參數同上 $.getScript(url, data, success) //以 GET 請求獲取一個 JS 文件並執行,參數含義同上 $.getJSON(url, data, success) //以 GET 請求獲取一個 JSON 字符串,參數含義同上
$().ajaxComplete(function(){}); //註冊Ajax請求完成時要調用的處理程序 $().ajaxError(function(){}); //註冊要在Ajax請求完成時遇到錯誤而調用的處理程序 $().ajaxSend(function(){}); //附加要在發送Ajax請求以前執行的函數 $().ajaxStart(function(){}); //註冊在第一個Ajax請求開始時要調用的處理程序 $().ajaxStop(function(){}); //註冊要在全部Ajax請求完成後調用的處理程序 $().ajaxSuccess(function(){}); //附加要在Ajax請求成功完成時執行的函數 $().load(url, data, callback); //返回某 url 的數據,data爲傳遞參數(可選), callback(reponseText, statusText, xhr) 回調函數(可選)
$.param(obj); //將對象轉化爲一個 url 參數列表 $(form).serialize(); //表單數據序列化爲 url 參數列表 $(form).serializeArray(); //同上,但返回 JSON 串
簡單模仿 jQuery 中 $.ajax()
方法
(function(){ // Ajax 選項 var options = { type: "GET", //提交方式 url: "", //路徑 params: {}, //請求參數 dataType: "text", //內容類型 success: function(){}, //回調函數 error: function(){} }; //獲取 XMLHTTPRequest 對象 var createRequest = function(){ var xmlhttp; if(xmlhttp.XMLHttpRequest){ xmlhttp = new XMLHttpRequest(); } else{ xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); } if(xmlhttp.overrideMimeType){ xmlhttp.overrideMimeType('text/xml'); //修改 MIME 類型 } return xmlhttp; }, // 設定 Ajax 選項 var setOptions = function(newOptions){ for(var prop in newOptions){ if(newOptions.hasOwnProperty(prop)){ this.option[prop] = newOptions[prop]; } } }, //格式化參數列表 var formatParameters = function(){ var paramsArr = []; var params = this.options.params; for(var prop in params){ if(params.hasOwnProperty(prop)){ paramsArr.push(prop + "=" + encodeURIComponent(params[prop])); } } return paramsArr.join('&'); }, //預處理並調用相應函數 var readystatechange = function(xmlhttp){ var returnValue; if(xmlhttp.readyState == 4 && xmlhttp.status == 200){ switch(this.options.dataType){ case 'xml': returnValue = xmlhttp.responseXML; break; case 'json': returnVaue = xmlhttp.responseText; if(returnValue){ returnValue = eval("(" + returnValue + ")"); } break; default: returnVaue = xmlhttp.responseText; break; } if(returnValue){ this.options.success(returnValue); } else{ this.options.success(); } } else{ this.options.error(); } }, //發送請求,也就是$.ajax()函數 var request = function(options){ // var ajaxObj = this; var xmlhttp = this.createRequest(); this.setOptions(options); xmlhttp.onreadystatechange = this.readystatechange.bind(null, xmlhttp); var formatParams = this.formatParameters(); var type = this.options.type; var url = this.options.url; if("GET" === type.toUpperCase()){ url += "?" + formatParameters; } xmlhttp.open(type, url, true); if("GET" === type.toUpperCase()){ xmlhttp.send(); } else if("POST" === type.toUpperCase()){ xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlhttp.send(formatParameters); } } window.$.ajax = request; //暴露方法到閉包外面去 })();