摘要:基本概念全稱是異步的和。通過(guò)在后臺(tái)與服務(wù)器進(jìn)行少量數(shù)據(jù)交換,可以使網(wǎng)頁(yè)實(shí)現(xiàn)異步更新。我們加了一個(gè)隨機(jī)數(shù)以后,每次請(qǐng)求不同,瀏覽器就不會(huì)使用緩存數(shù)據(jù)了。中文亂碼問(wèn)題返回的中文數(shù)據(jù)亂碼是因?yàn)轫?yè)面和頁(yè)面中使用了不同的編碼方式導(dǎo)致的。
基本概念
Ajax 全稱是異步的 JavaScript 和 XML 。 通過(guò)在后臺(tái)與服務(wù)器進(jìn)行少量數(shù)據(jù)交換,AJAX 可以使網(wǎng)頁(yè)實(shí)現(xiàn)異步更新。這意味著可以在不重新加載整個(gè)網(wǎng)頁(yè)的情況下,對(duì)網(wǎng)頁(yè)的某部分進(jìn)行更新。傳統(tǒng)的網(wǎng)頁(yè)(不使用 AJAX)如果需要更新內(nèi)容,必須重載整個(gè)網(wǎng)頁(yè)頁(yè)面。
Ajax 具有以下優(yōu)點(diǎn)和缺點(diǎn):
優(yōu)點(diǎn)
無(wú)需刷新頁(yè)面,用戶體驗(yàn)好;
異步與服務(wù)器通信,不影響主進(jìn)程,響應(yīng)更迅速;
可以把部分服務(wù)器的工作放在客戶端的瀏覽器完成,減輕服務(wù)器壓力,減少冗余請(qǐng)求和響應(yīng);
Ajax 是前端開(kāi)發(fā)的標(biāo)準(zhǔn)化技術(shù),無(wú)需插件支持,跨平臺(tái)性能好;
缺點(diǎn)
Ajax 請(qǐng)求不修改瀏覽器歷史記錄,因此不支持前進(jìn)后退功能;
Ajax 暴露了過(guò)多和服務(wù)器交互的細(xì)節(jié);
破壞了程序的異常機(jī)制,容易調(diào)試;
不利于搜索引擎抓取信息;
同源策略同源策略是Netscape提出的一個(gè)著名的安全策略,它是指同一個(gè)“源頭”的數(shù)據(jù)可以自由訪問(wèn),但不同源的數(shù)據(jù)相互之間都不能訪問(wèn)。我們?cè)囅胍幌乱韵聨追N情況:
我們打開(kāi)了一個(gè)天貓并且登錄了自己的賬號(hào),這時(shí)我們?cè)俅蜷_(kāi)一個(gè)天貓的商品,我們不需要再進(jìn)行一次登錄就可以直接購(gòu)買(mǎi)商品,因?yàn)檫@兩個(gè)網(wǎng)頁(yè)是同源的,可以共享登錄相關(guān)的 cookie 或 localStorage 數(shù)據(jù);
如果你正在用支付寶或者網(wǎng)銀,同時(shí)打開(kāi)了一個(gè)不知名的網(wǎng)頁(yè),如果這個(gè)網(wǎng)頁(yè)可以訪問(wèn)你支付寶或者網(wǎng)銀頁(yè)面的信息,就會(huì)產(chǎn)生嚴(yán)重的安全的問(wèn)題。顯然瀏覽器不允許這樣的事情發(fā)生;
想必你也有過(guò)同時(shí)登陸好幾個(gè) qq 賬號(hào)的情況,如果同時(shí)打開(kāi)各自的 qq 空間瀏覽器會(huì)有一個(gè)小號(hào)模式,也就是另外再打開(kāi)一個(gè)窗口專門(mén)用來(lái)打開(kāi)第二個(gè) qq 賬號(hào)的空間。
很明顯,第1個(gè)和第3個(gè)例子中,不同的天貓商店和 qq 空間屬于同源,可以共享登錄信息。qq 為了區(qū)別不同的 qq 的登錄信息,重新打開(kāi)了一個(gè)窗口,因?yàn)闉g覽器的不同窗口是不能共享信息的。而第2個(gè)例子中的支付寶、網(wǎng)銀、不知名網(wǎng)站之間是非同源的,所以彼此之間無(wú)法訪問(wèn)信息,如果你執(zhí)意想請(qǐng)求數(shù)據(jù),會(huì)提示異常:
No "Access-Control-Allow-Origin" header is present on the requested resource. Origin "null" is therefore not allowed access.
那么什么是同源的請(qǐng)求呢?同源請(qǐng)求要求被請(qǐng)求資源頁(yè)面和發(fā)出請(qǐng)求頁(yè)面滿足3個(gè)相同:
協(xié)議相同
域名相同
端口相同
簡(jiǎn)單理解一下:
/*以下兩個(gè)數(shù)據(jù)非同源,因?yàn)閰f(xié)議不同*/ http://www.abc123.com.cn/item/a.js https://www.abc123.com.cn/item/a.js /*以下兩個(gè)數(shù)據(jù)非同源,因?yàn)橛蛎煌?/ http://www.abc123.com.cn/item/a.js http://www.abc123.com/item/a.js /*以下兩個(gè)數(shù)據(jù)非同源,因?yàn)橹鳈C(jī)名不同*/ http://www.abc123.com.cn/item/a.js http://item.abc123.com.cn/item/a.js /*以下兩個(gè)數(shù)據(jù)非同源,因?yàn)閰f(xié)議不同*/ http://www.abc123.com.cn/item/a.js http://www.abc123.com.cn:8080/item/a.js /* 以下兩個(gè)數(shù)據(jù)非同源,域名和 ip 視為不同源 * 這里應(yīng)注意,ip和域名替換一樣不是同源的 * 假設(shè)www.abc123.com.cn解析后的 ip 是 195.155.200.134 */ http://www.abc123.com.cn/ http://195.155.200.134/ /*以下兩個(gè)數(shù)據(jù)同源*/ /* 這個(gè)是同源的*/ http://www.abc123.com.cn/source/a.html http://www.abc123.com.cn/item/b.jsAjax
Ajax在編寫(xiě)時(shí)一共4個(gè)步驟:
創(chuàng)建 xhr 對(duì)象
設(shè)置傳輸?shù)刂?/p>
設(shè)置回調(diào)函數(shù)
發(fā)送數(shù)據(jù)
常見(jiàn)的發(fā)送方式有 GET 和 POST,除此之外還有 HEAD, DELETE, TRACE, PUT, CONNECT, OPTIONS和 PATCH等,這里只舉例前兩個(gè) GET 和 POST。
例如根據(jù)姓名查詢一個(gè)人的信息并寫(xiě)在div#output中
//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 + "
性別:" + data.gender + "
年齡:" + data.age + "
地址:" + data.address + "
電話:" + data.tel; } var output = document.getElementById("output"); search("李華", show.bind(output)); //服務(wù)器端 search.php
//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 設(shè)置為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 + "
性別:" + data.gender + "
年齡:" + data.age + "
地址:" + data.address + "
電話:" + data.tel; } var output = document.getElementById("output"); search("李華", show.bind(output)); //服務(wù)器端 search.php
上述代碼的 jQuery 寫(xiě)法:
//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 + "
性別:" + data.gender + "
年齡:" + data.age + "
地址:" + data.address + "
電話:" + 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 + "Ajax常見(jiàn)問(wèn)題 緩存問(wèn)題
性別:" + data.gender + "
年齡:" + data.age + "
地址:" + data.address + "
電話:" + data.tel; } var output = document.getElementById("output"); search("李華", show.bind(output));
細(xì)心一些可以發(fā)現(xiàn),上面發(fā)送請(qǐng)求的數(shù)據(jù)中加入了一個(gè)隨機(jī)數(shù) t。因?yàn)橛袝r(shí)服務(wù)器更新的了數(shù)據(jù)后,我們?cè)僖淮螆?zhí)行 Ajax 請(qǐng)求不能顯示新的結(jié)果,這是由于 js 為了加速,頁(yè)面會(huì)使用緩存保持當(dāng)前調(diào)用的相同鏈接。我們加了一個(gè)隨機(jī)數(shù)以后,每次請(qǐng)求不同,瀏覽器就不會(huì)使用緩存數(shù)據(jù)了。
中文亂碼問(wèn)題返回的中文數(shù)據(jù)亂碼是因?yàn)?js 頁(yè)面和action頁(yè)面中使用了不同的編碼方式導(dǎo)致的??梢杂幸韵?中方式解決(瀏覽器 html 文件是 urf-8 編碼的):
對(duì)請(qǐng)求數(shù)據(jù)字段進(jìn)行2次 encodeURI 編碼,服務(wù)器獲取數(shù)據(jù)后做一次 UTF-8 轉(zhuǎn)碼
對(duì)請(qǐng)求數(shù)據(jù)字段進(jìn)行1次 encodeURI 編碼,服務(wù)器獲取數(shù)據(jù)后做一次 ISO-8859-1 轉(zhuǎn)換 和一次 UTF-8 轉(zhuǎn)碼
tips: 考慮到兼容性,第1個(gè)方法更好
兼容性問(wèn)題之前的代碼并沒(méi)有按兼容性的格式書(shū)寫(xiě),不過(guò) Ajax 的兼容也不難,主要表現(xiàn)在 XMLHTTPRequest對(duì)象獲取環(huán)節(jié):
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方式對(duì)比
--- | GET | POST |
---|---|---|
后退/刷新 | 無(wú)害 | 數(shù)據(jù)會(huì)重新提交 |
書(shū)簽 | 可藏為書(shū)簽 | 無(wú)法藏為書(shū)簽 |
緩存 | 可以緩存 | 不可以緩存 |
MIME類(lèi)型 | application/x-www-from-urlencode | application/x-www-from-urlencode或 multipart/form-data (二進(jìn)制為多重編碼 |
歷史記錄 | 參數(shù)保留在歷史記錄中 | 參數(shù)不會(huì)留在歷史記錄 |
數(shù)據(jù)長(zhǎng)度 | URL最長(zhǎng)2048個(gè)字符(2kB) | 無(wú)限 |
數(shù)據(jù)類(lèi)型 | ASCII字符 | 無(wú)限 |
安全性 | 差 | 較 |
可見(jiàn)性 | 數(shù)據(jù)可見(jiàn) | 數(shù)據(jù)不可見(jiàn) |
這里需要強(qiáng)調(diào)的是,jsonp不屬于Ajax的部分,它只是吧url放入script標(biāo)簽中實(shí)現(xiàn)的數(shù)據(jù)傳輸,主要優(yōu)點(diǎn)是不受同源策略限制。由于一般庫(kù)也會(huì)把它和Ajax封裝在一起,所以這里放在一起討論。下面是一個(gè)jsonp的例子(實(shí)現(xiàn)功能:輸入手機(jī)號(hào)碼查詢歸屬地和運(yùn)營(yíng)商):
兼容問(wèn)題
上述代碼的全部js部分可以用jQuery實(shí)現(xiàn),如下:
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跨域問(wèn)題(本質(zhì)已不是Ajax) success: function(data){ $("#output").text(data.province + " " + data.catName); } }); } else { alert("您輸入的手機(jī)號(hào)有誤") } });其他 Ajax 參數(shù)及方法
javascript
//屬性 xhr.responseText; //從服務(wù)器返回的字符串?dāng)?shù)據(jù) xhr.responseXML; //從服務(wù)器返回的 XML 數(shù)據(jù) xhr.status; //服務(wù)器相應(yīng)狀態(tài) xhr.readyState; //0: 請(qǐng)求未初始化; 1: 已建立連接; 2: 請(qǐng)求已接收; 3: 請(qǐng)求處理中; 4: 響應(yīng)已就緒 xhr.timeout; //指定多少毫秒后超時(shí),長(zhǎng)整型 xhr.upload; //獲取上傳進(jìn)度 xhr.withCredentials; //是否可以跨源,boolean 型,默認(rèn) false //方法 xhr.getResponseHeader("connection"); //獲取指定頭信息 xhr.getAllResponseHeaders(); //獲全部定頭信息 xhr.open("METHOD", url, isAsyn); //open方法有3個(gè)參數(shù),最后一個(gè)參數(shù)是 Boolean 型,表示是否異步,默認(rèn)為 true xhr.abort(); //終止請(qǐng)求,置xhr.readyState為0,但不觸發(fā)onreadystatechange xhr.overrideMimeType() //強(qiáng)制重寫(xiě) http 頭的 MIME 類(lèi)型 //事件 XMLHttpRequestEventTarget.onreadystatechange //在xhr.readyState屬性改變時(shí)觸發(fā) XMLHttpRequestEventTarget.ontimeout //在響應(yīng)超時(shí)時(shí)觸發(fā) XMLHttpRequestEventTarget.onabort //當(dāng)請(qǐng)求失敗時(shí)調(diào)用該方法 XMLHttpRequestEventTarget.onerror //當(dāng)請(qǐng)求發(fā)生錯(cuò)誤時(shí)調(diào)用該方法 XMLHttpRequestEventTarget.onload //當(dāng)一個(gè)HTTP請(qǐng)求正確加載出內(nèi)容后返回時(shí)調(diào)用。 XMLHttpRequestEventTarget.onloadstart //當(dāng)一個(gè)HTTP請(qǐng)求開(kāi)始加載數(shù)據(jù)時(shí)調(diào)用。 XMLHttpRequestEventTarget.onloadend //當(dāng)內(nèi)容加載完成,不管失敗與否,都會(huì)調(diào)用該方法 XMLHttpRequestEventTarget.onprogress //間歇調(diào)用該方法用來(lái)獲取請(qǐng)求過(guò)程中的信息。
注:關(guān)于 xhr.status 可能的返回值,詳見(jiàn) http狀態(tài)碼
jQuery 中的 Ajax 方法 ajax 靜態(tài)方法$.ajax({options}) //發(fā)起一個(gè) ajax 請(qǐng)求 options 常用以下屬性設(shè)置:url, method("GET"/"POST"), crossDomain, accepts(可接受的類(lèi)型), dataType, cache, contentType(編碼格式), success, error等 $.ajaxSetup({options}); //options同上,設(shè)置 ajax 默認(rèn)參數(shù),不建議使用 $.post(url, data, success, datatype); //發(fā)起一個(gè) POST 請(qǐng)求 data為傳遞參數(shù)(可選), success(reponseText, statusText, xhr) 為成功時(shí)的回調(diào)函數(shù)(可選), datatype(xml/html/script/json/jsonp/text,可選) $.get(url, data, success, datatype); //發(fā)起一個(gè) GET 請(qǐng)求, 參數(shù)同上 $.getScript(url, data, success) //以 GET 請(qǐng)求獲取一個(gè) JS 文件并執(zhí)行,參數(shù)含義同上 $.getJSON(url, data, success) //以 GET 請(qǐng)求獲取一個(gè) JSON 字符串,參數(shù)含義同上ajax 動(dòng)態(tài)方法
$().ajaxComplete(function(){}); //注冊(cè)Ajax請(qǐng)求完成時(shí)要調(diào)用的處理程序 $().ajaxError(function(){}); //注冊(cè)要在Ajax請(qǐng)求完成時(shí)遇到錯(cuò)誤而調(diào)用的處理程序 $().ajaxSend(function(){}); //附加要在發(fā)送Ajax請(qǐng)求之前執(zhí)行的函數(shù) $().ajaxStart(function(){}); //注冊(cè)在第一個(gè)Ajax請(qǐng)求開(kāi)始時(shí)要調(diào)用的處理程序 $().ajaxStop(function(){}); //注冊(cè)要在所有Ajax請(qǐng)求完成后調(diào)用的處理程序 $().ajaxSuccess(function(){}); //附加要在Ajax請(qǐng)求成功完成時(shí)執(zhí)行的函數(shù) $().load(url, data, callback); //返回某 url 的數(shù)據(jù),data為傳遞參數(shù)(可選), callback(reponseText, statusText, xhr) 回調(diào)函數(shù)(可選)其他相關(guān)方法
$.param(obj); //將對(duì)象轉(zhuǎn)化為一個(gè) url 參數(shù)列表 $(form).serialize(); //表單數(shù)據(jù)序列化為 url 參數(shù)列表 $(form).serializeArray(); //同上,但返回 JSON 串簡(jiǎn)單封裝 Ajax 相關(guān)方法
簡(jiǎn)單模仿 jQuery 中 $.ajax() 方法
(function(){ // Ajax 選項(xiàng) var options = { type: "GET", //提交方式 url: "", //路徑 params: {}, //請(qǐng)求參數(shù) dataType: "text", //內(nèi)容類(lèi)型 success: function(){}, //回調(diào)函數(shù) error: function(){} }; //獲取 XMLHTTPRequest 對(duì)象 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 類(lèi)型 } return xmlhttp; }, // 設(shè)定 Ajax 選項(xiàng) var setOptions = function(newOptions){ for(var prop in newOptions){ if(newOptions.hasOwnProperty(prop)){ this.option[prop] = newOptions[prop]; } } }, //格式化參數(shù)列表 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("&"); }, //預(yù)處理并調(diào)用相應(yīng)函數(shù) 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(); } }, //發(fā)送請(qǐng)求,也就是$.ajax()函數(shù) 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; //暴露方法到閉包外面去 })();
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/97335.html