前言
在日常工作中,我們經常使用Jquery的Ajax來獲取接口數據。但是有的時候項目里面用不到Jquery,為了減少加載Jquery庫的時間。也不用負擔Jquery復雜的邏輯處理帶來的性能消耗,所以我打算封裝一個源生的Ajax函數
需求整理
一般來說前端與服務器的通信是使用XHR的,但是我希望封裝的函數能有跨域的功能。所以我把JSONP整合進來了.
下面看主體功能圖
功能圖.png
輸入參數
首先必須要定義個AJAX函數,并設置一些輸入參數
function ajax(options){
var url = options.url || "", //請求的鏈接
type = (options.type || "get").toLowerCase(), //請求的方法,默認為get
data = options.data || null, //請求的數據
contentType = options.contentType || "", //請求頭
dataType = options.dataType || "jsonp",
//請求的類型要是jsonp他利用得get 問號后面是?callback = 執行成功的方法 callback可以依照規矩修改
async = options.async === undefined && true, //是否異步,默認為true.
timeOut = options.timeOut, //超時時間。
before = options.before || function(){}, //發送之前執行的函數
error = options.error || function(){}, //錯誤執行的函數
success = options.success || function() {}; //請求成功的回調函數
}
參數表
參數表.jpg
調用
//var json = "name=garfield&age=18"; //字符串
var json ={"haha":[{"name":"張三"},{"age":"23"}]}; //大JSON包
ajax({
type:"post", //發送數據的類型
url:"index.php", //添加自己的接口鏈接
timeOut:5000, //過期時間
data:json, //發送的數據
before:function(){
console.log("發送成功") //發送之前要做的事情
},
success:function(str){ //接收到數據成功后
console.log(str);
},
error:function(status,statusText){ //如果出現錯誤后,第一個表示錯誤的狀態 第二個表示錯誤的原因
console.log(status+"||"+statusText);
}
})
PHP接收
<?php
header("content-type:text/html; charset=utf-8");
$name = $_POST["haha"]; //對應的就是JSON的name
echo($name[0]["name"]);
?>
編碼
一般來說發送到后端的數據,若是包括中文或者某些標點符號,就要對數據進行編碼
- 如果data為字符串,通過&分割,對鍵名與鍵值分別編碼
- 如果data為對象,把鍵值轉化為字符串,在進行編碼
- 由于encodeURIComponent不對+編碼,所以只有使用replace方法手動編碼
- 若是使用get方法或者JSONP,則數據是通過URL參數傳到后臺,所以手動添加數據到URL
//編碼數據
function setData() {
//設置對象的遍碼
function setObjData(data, parentName) {
function encodeData(name, value, parentName) {
var items = [];
name = parentName === undefined ? name : parentName + "[" + name + "]";
if (typeof value === "object" && value !== null) {
items = items.concat(setObjData(value, name));
} else {
name = encodeURIComponent(name);
value = encodeURIComponent(value);
items.push(name + "=" + value);
}
return items;
}
var arr = [],value;
if (Object.prototype.toString.call(data) == '[object Array]') {
for (var i = 0, len = data.length; i < len; i++) {
value = data[i];
arr = arr.concat(encodeData( typeof value == "object"?i:"", value, parentName));
}
} else if (Object.prototype.toString.call(data) == '[object Object]') {
for (var key in data) {
value = data[key];
arr = arr.concat(encodeData(key, value, parentName));
}
}
return arr;
};
//設置字符串的遍碼,字符串的格式為:a=1&b=2;
function setStrData(data) {
var arr = data.split("&");
for (var i = 0, len = arr.length; i < len; i++) {
name = encodeURIComponent(arr[i].split("=")[0]);
value = encodeURIComponent(arr[i].split("=")[1]);
arr[i] = name + "=" + value;
}
return arr;
}
if (data) {
if (typeof data === "string") {
data = setStrData(data);
} else if (typeof data === "object") {
data = setObjData(data);
}
data = data.join("&").replace("/%20/g", "+");
//若是使用get方法或JSONP,則手動添加到URL中
if (type === "get" || dataType === "jsonp") {
url += url.indexOf("?") > -1 ? (url.indexOf("=") > -1 ? "&" + data : data) : "?" + data;
}
}
}
XMLHttpRequerst
- 創建XHR對象 ,并對IE進行兼容性處理
- 調用XHR的open方法,設置請求的方法,請求的鏈接,是否異步
- 設置請求頭
- 添加監聽,如果成功則返回success函數,報錯則執行error函數
- 調用XHR的send方法,發送數據。如果get方法,我們通過setData方法把數據添加到URL中了,這里data設置為null
// XHR
function createXHR() {
//由于IE6的XMLHttpRequest對象是通過MSXML庫中的一個ActiveX對象實現的。
//所以創建XHR對象,需要在這里做兼容處理。
function getXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
//遍歷IE中不同版本的ActiveX對象
var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"];
for (var i = 0; i < versions.length; i++) {
try {
var version = versions[i] + ".XMLHTTP";
return new ActiveXObject(version);
} catch (e) {}
}
}
}
//創建對象。
xhr = getXHR();
xhr.open(type, url, async);
//設置請求頭
if (type === "post" && !contentType) {
//若是post提交,則設置content-Type 為application/x-www-four-urlencoded
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
} else if (contentType) {
xhr.setRequestHeader("Content-Type", contentType);
}
//添加監聽
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (timeOut !== undefined) {
//由于執行abort()方法后,有可能觸發onreadystatechange事件,
//所以設置一個timeout_bool標識,來忽略中止觸發的事件。
if (timeout_bool) {
return;
}
clearTimeout(timeout_flag);
}
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
success(xhr.responseText);
} else {
error(xhr.status, xhr.statusText);
}
}
};
//發送請求
xhr.send(type === "get" ? null : data);
setTime(); //請求超時
}
JSONP
- 創建script標簽
- 設置回調函數名稱
- 監聽回調函數
- 設置URL,并添加到文檔中
function createJsonp() {
var script = document.createElement("script"),
timeName = new Date().getTime() + Math.round(Math.random() * 1000),
callback = "JSONP_" + timeName;
window[callback] = function(data) {
clearTimeout(timeout_flag);
document.body.removeChild(script);
success(data);
}
script.src = url + (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback;
script.type = "text/javascript";
document.body.appendChild(script);
setTime(callback, script);
}
超時請求
- 設置一個全局的定時器標識,用來在回調函數中清除定時器
- JSONP
- 傳入兩個參數,一個是回調函數名,一個是script標簽
- 超時以后,移除監聽函數,移除script標簽
- XHR
- 超時之后,調用XHR的about方法,停止請求
- 由于執行about()方法后,有可能觸發onreadystatechange事件,所以設置一個timeout_bool標識,來忽略終止觸發的事件.
//設置請求超時
function setTime(callback, script) {
if (timeOut !== undefined) {
timeout_flag = setTimeout(function() {
if (dataType === "jsonp") {
delete window[callback];
document.body.removeChild(script);
} else {
timeout_bool = true;
xhr && xhr.abort();
}
console.log("timeout");
}, timeOut);
}
}
全部代碼
/**
* Created by Administrator on 2017/4/12.
* 自己封裝了一個AJAX小類庫
*/
(function(window,undefined) {
function ajax(options) {
//編碼數據
function setData() {
//設置對象的遍碼
function setObjData(data, parentName) {
function encodeData(name, value, parentName) {
var items = [];
name = parentName === undefined ? name : parentName + "[" + name + "]";
if (typeof value === "object" && value !== null) {
items = items.concat(setObjData(value, name));
} else {
name = encodeURIComponent(name);
value = encodeURIComponent(value);
items.push(name + "=" + value);
}
return items;
}
var arr = [],value;
if (Object.prototype.toString.call(data) == '[object Array]') {
for (var i = 0, len = data.length; i < len; i++) {
value = data[i];
arr = arr.concat(encodeData( typeof value == "object"?i:"", value, parentName));
}
} else if (Object.prototype.toString.call(data) == '[object Object]') {
for (var key in data) {
value = data[key];
arr = arr.concat(encodeData(key, value, parentName));
}
}
return arr;
};
//設置字符串的遍碼,字符串的格式為:a=1&b=2;
function setStrData(data) {
var arr = data.split("&");
for (var i = 0, len = arr.length; i < len; i++) {
name = encodeURIComponent(arr[i].split("=")[0]);
value = encodeURIComponent(arr[i].split("=")[1]);
arr[i] = name + "=" + value;
}
return arr;
}
if (data) {
if (typeof data === "string") {
data = setStrData(data);
} else if (typeof data === "object") {
data = setObjData(data);
}
data = data.join("&").replace("/%20/g", "+");
//若是使用get方法或JSONP,則手動添加到URL中
if (type === "get" || dataType === "jsonp") {
url += url.indexOf("?") > -1 ? (url.indexOf("=") > -1 ? "&" + data : data) : "?" + data;
}
}
}
// JSONP
function createJsonp() {
var script = document.createElement("script"),
timeName = new Date().getTime() + Math.round(Math.random() * 1000),
callback = "JSONP_" + timeName;
window[callback] = function(data) {
clearTimeout(timeout_flag);
document.body.removeChild(script);
success(data);
}
script.src = url + (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback;
script.type = "text/javascript";
document.body.appendChild(script);
setTime(callback, script);
}
//設置請求超時
function setTime(callback, script) {
if (timeOut !== undefined) {
timeout_flag = setTimeout(function() {
if (dataType === "jsonp") {
delete window[callback];
document.body.removeChild(script);
} else {
timeout_bool = true;
xhr && xhr.abort();
}
console.log("timeout");
}, timeOut);
}
}
// XHR
function createXHR() {
//由于IE6的XMLHttpRequest對象是通過MSXML庫中的一個ActiveX對象實現的。
//所以創建XHR對象,需要在這里做兼容處理。
function getXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
//遍歷IE中不同版本的ActiveX對象
var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"];
for (var i = 0; i < versions.length; i++) {
try {
var version = versions[i] + ".XMLHTTP";
return new ActiveXObject(version);
} catch (e) {}
}
}
}
//創建對象。
xhr = getXHR();
xhr.open(type, url, async);
//設置請求頭
if (type === "post" && !contentType) {
//若是post提交,則設置content-Type 為application/x-www-four-urlencoded
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
} else if (contentType) {
xhr.setRequestHeader("Content-Type", contentType);
}
//添加監聽
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (timeOut !== undefined) {
//由于執行abort()方法后,有可能觸發onreadystatechange事件,
//所以設置一個timeout_bool標識,來忽略中止觸發的事件。
if (timeout_bool) {
return;
}
clearTimeout(timeout_flag);
}
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
success(xhr.responseText);
} else {
error(xhr.status, xhr.statusText);
}
}
};
//發送請求
xhr.send(type === "get" ? null : data);
setTime(); //請求超時
}
var url = options.url || "", //請求的鏈接
type = (options.type || "get").toLowerCase(), //請求的方法,默認為get
data = options.data || null, //請求的數據
contentType = options.contentType || "", //請求頭
dataType = options.dataType || "", //請求的類型
async = options.async === undefined ? true : options.async, //是否異步,默認為true.
timeOut = options.timeOut, //超時時間。
before = options.before || function() {}, //發送之前執行的函數
error = options.error || function() {}, //錯誤執行的函數
success = options.success || function() {}; //請求成功的回調函數
var timeout_bool = false, //是否請求超時
timeout_flag = null, //超時標識
xhr = null; //xhr對角
setData();
before();
if (dataType === "jsonp") {
createJsonp();
} else {
createXHR();
}
}
window.ajax = ajax;
})(window);