一、JS原生Ajax
ajax:一種請求數據的方式,不需要刷新整個頁面;
ajax的技術核心是 XMLHttpRequest 對象;
ajax 請求過程:創建 XMLHttpRequest 對象、連接服務器、發送請求、接收響應數據;
下面簡單封裝一個函數,之後稍作解釋
ajax({
url: "./TestXHR.aspx", //請求地址
type: "POST", //請求方式
data: { name: "super", age: 20 }, //請求參數
dataType: "json",
success: function (response, xml) {
// 此處放成功後執行的代碼
},
fail: function (status) {
// 此處放失敗後執行的代碼
}
});
function ajax(options) {
options = options || {};
options.type = (options.type || "GET").toUpperCase();
options.dataType = options.dataType || "json";
var params = formatParams(options.data);
//創建 - 非IE6 - 第一步
if (window.XMLHttpRequest) {
var xhr = new XMLHttpRequest();
} else { //IE6及其以下版本瀏覽器
var xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
//接收 - 第三步
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
var status = xhr.status;
if (status >= 200 && status < 300) {
options.success && options.success(xhr.responseText, xhr.responseXML);
} else {
options.fail && options.fail(status);
}
}
}
//連接 和 發送 - 第二步
if (options.type == "GET") {
xhr.open("GET", options.url + "?" + params, true);
xhr.send(null);
} else if (options.type == "POST") {
xhr.open("POST", options.url, true);
//設置表單提交時的內容類型
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(params);
}
}
//格式化參數
function formatParams(data) {
var arr = [];
for (var name in data) {
arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
}
arr.push(("v=" + Math.random()));
return arr.join("&");
}
1、創建
1.1、IE7及其以上版本中支持原生的 XHR 對象,因此可以直接用: var oAjax = new XMLHttpRequest();
1.2、IE6及其之前的版本中,XHR對象是通過MSXML庫中的一個ActiveX對象實現的。有的書中細化了IE中此類對象的三種不同版本,即MSXML2.XMLHttp、MSXML2.XMLHttp.3.0 和 MSXML2.XMLHttp.6.0;個人感覺太麻煩,可以直接使用下面的語句創建: var oAjax=new ActiveXObject(’Microsoft.XMLHTTP’);
2、連接和發送
2.1、open()函數的三個參數:請求方式、請求地址、是否異步請求(同步請求的情況極少,至今還沒用到過);
2.2、GET 請求方式是通過URL參數將數據提交到服務器的,POST則是通過將數據作爲 send 的參數提交到服務器;
2.3、POST 請求中,在發送數據之前,要設置表單提交的內容類型;
2.4、提交到服務器的參數必須經過 encodeURIComponent() 方法進行編碼,實際上在參數列表”key=value”的形式中,key 和 value 都需要進行編碼,因爲會包含特殊字符。每次請求的時候都會在參數列表中拼入一個 “v=xx” 的字符串,這樣是爲了拒絕緩存,每次都直接請求到服務器上。
encodeURI() :用於整個 URI 的編碼,不會對本身屬於 URI 的特殊字符進行編碼,如冒號、正斜槓、問號和井號;其對應的解碼函數 decodeURI();
encodeURIComponent() :用於對 URI 中的某一部分進行編碼,會對它發現的任何非標準字符進行編碼;其對應的解碼函數 decodeURIComponent();
3、接收
3.1、接收到響應後,響應的數據會自動填充XHR對象,相關屬性如下
responseText:響應返回的主體內容,爲字符串類型;
responseXML:如果響應的內容類型是 “text/xml” 或 “application/xml”,這個屬性中將保存着相應的xml 數據,是 XML 對應的 document 類型;
status:響應的HTTP狀態碼;
statusText:HTTP狀態的說明;
3.2、XHR對象的readyState屬性表示請求/響應過程的當前活動階段,這個屬性的值如下
0-未初始化,尚未調用open()方法;
1-啓動,調用了open()方法,未調用send()方法;
2-發送,已經調用了send()方法,未接收到響應;
3-接收,已經接收到部分響應數據;
4-完成,已經接收到全部響應數據;
只要 readyState 的值變化,就會調用 readystatechange 事件,(其實爲了邏輯上通順,可以把readystatechange放到send之後,因爲send時請求服務器,會進行網絡通信,需要時間,在send之後指定readystatechange事件處理程序也是可以的,我一般都是這樣用,但爲了規範和跨瀏覽器兼容性,還是在open之前進行指定吧)。
3.3、在readystatechange事件中,先判斷響應是否接收完成,然後判斷服務器是否成功處理請求,xhr.status 是狀態碼,狀態碼以2開頭的都是成功,304表示從緩存中獲取,上面的代碼在每次請求的時候都加入了隨機數,所以不會從緩存中取值,故該狀態不需判斷。
4、ajax請求是不能跨域的!
二、JSONP
JSONP(JSON with Padding) 是一種跨域請求方式。主要原理是利用了script 標籤可以跨域請求的特點,由其 src 屬性發送請求到服務器,服務器返回 js 代碼,網頁端接受響應,然後就直接執行了,這和通過 script 標籤引用外部文件的原理是一樣的。
JSONP由兩部分組成:回調函數和數據,回調函數一般是由網頁端控制,作爲參數發往服務器端,服務器端把該函數和數據拼成字符串返回。
比如網頁端創建一個 script 標籤,並給其 src 賦值爲 http://localhost:8080/ac/test.php?callback=foo
, 此時網頁端就發起一個請求。服務端將要返回的數據拼好最爲函數的參數傳入,服務端返回的數據格式類似foo({"name":"OK"})
,網頁端接收到了響應值,因爲請求者是 script,所以相當於直接調用 process 方法,並且傳入了一個參數。
單看響應返回的數據,JSONP 比 ajax 方式就多了一個回調函數。
jsonp({
url: "http://localhost:8080/ac/test.php",
data: { q: 1 },
callbackName: "foo",
time:1,
success: function (json) {
// 此處放成功後執行的代碼
console.log(json.name);
},
error: function(json){
console.log(json.message);
}
});
function jsonp(options) {
options = options || {};
options.callback = options.callback || "callback";
if (!options.url || !options.callbackName) {
throw new Error("參數不合法");
}
//創建 script 標籤並加入到頁面中
var callbackName = (options.callbackName + Math.random()).replace(".", "");
options.data[options.callback] = callbackName;
var params = formatParams(options.data);
var oHead = document.getElementsByTagName('head')[0];
var oS = document.createElement('script');
oHead.appendChild(oS);
//創建jsonp回調函數
callbackName = callbackName.split("0")[0];
window[callbackName] = function (json) {
oHead.removeChild(oS);
clearTimeout(oS.timer);
window[callbackName] = null;
options.success && options.success(json);
};
//發送請求
oS.src = options.url + '?' + params;
//超時處理
if (options.time) {
oS.timer = setTimeout(function () {
window[callbackName] = null;
oHead.removeChild(oS);
options.error && options.error({ message: "超時" });
}, options.time);
}
};
//格式化參數
function formatParams(data) {
var arr = [];
for (var name in data) {
arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
}
return arr.join('&');
}
//test.php
<?php
$callback = $_GET["callback"];
$callback = explode("0",$callback);
$callback = $callback[0];
$array = [
"foo" => ["name" => "OK"],
"aaa" => "bar"
];
$result = $array[$callback];
echo $callback.'('.json_encode($result).')';
exit;
?>
1、因爲 script 標籤的 src 屬性只在第一次設置的時候起作用,導致 script 標籤沒法重用,所以每次完成操作之後要移除;
2、JSONP這種請求方式中,參數依舊需要編碼;
3、如果不設置超時,就無法得知此次請求是成功還是失敗;
三、Promise
Promise是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise對象。
所謂Promise,簡單說就是一個容器,裏面保存着某個未來纔會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise是一個對象,從它可以獲取異步操作的消息。Promise提供統一的API,各種異步操作都可以用同樣的方法進行處理。
var getJSON = function(url) {
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
function handler() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON("https://api.github.com/search/users?q=1").then(function(json) {
console.log('Contents: ' + json.items[0].id);
}, function(error) {
console.error('出錯了', error);
});