ajax
請求能夠說是在前端開發工做中必不可少的一個東西html
一、ajax
請求原理前端
ajax
技術的核心是XMLHttpRequest
對象,其經過建立一個XMLHttpRequest
對象,利用對象的open
方法發送請求,判斷對象中readyState
屬性(請求、響應過程的當前活動階段)的值和status
(Http的響應狀態)的值,獲得responseText
等open
方法的三個參數
URL
readyState
屬性的值
open()
方法open()
方法,但還沒有調用send()
方法。send()
方法,但還沒有接收到響應。二、jQuery
、小程序風格的ajax
請求封裝 jQuery
風格的代碼樣式node
$.ajax({
url: xxx,
success: () => {},
fail: err => {},
})
複製代碼
由上,咱們知道在封裝函數的時候,首先,它接收的是一個對象,因此,第一步:ajax
const ajax = ({}) => {}
複製代碼
接下來,咱們定義對象內部須要接收的參數,有:url
(請求的地址)、data
(發送的數據)、method
(請求方式)、header
(請求頭部信息)、success
(請求成功回調函數)、fail
(請求失敗回調函數)、async
(請求是否異步)、timeout
(設置請求超時時間)、onTimeOut
(超時處理回調函數)、...小程序
const ajax = ({
url,
data = {},
method = 'get', // 默認爲'get'請求
header,
async = true, // 默認爲異步請求
timeout,
success,
fail,
}) => {}
複製代碼
咱們一般會使用get
請求向服務器查詢一些信息,也一般會在請求的url
後面拼接上數據,像這樣:api
http://www.baidu.com?a=b&c=d...
複製代碼
那咱們如何實現呢?咱們定義一個拼接url
函數,須要兩個參數,一個是自己的url
,另一個是向後臺發送的數據param
,因此:promise
// 數據拼接url
addURL = (url, param) => {
if(param && Object.keys(param).length) { // 數據不爲空
// 判斷url後添加的字符是'?'仍是'&'
url += (url.indexOf('?') === -1 ? '?' : '&');
// 拼接數據
Object.keys(param).map(key => {
url += `${key}=${param[key]}`
})
}
return url;
}
複製代碼
一般呢,咱們使用get
方法會遇到查詢字符串格式錯誤的問題,因此,這時須要咱們用encodeURIComponent()
進行編碼,上面代碼改變以下:瀏覽器
addURL = (url, param) => {
if(param && Object.keys(param).length) {
url += (url.indexOf('?') === -1 ? '?' : '&');
Object.keys(param).map(key => {
url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
})
}
}
複製代碼
若是使用的是post
方法,咱們只須要將數據給服務端傳遞過去,最終,咱們寫出了封裝後的代碼:bash
const ajax = ({
url,
data = {},
method = 'get', // 默認爲'get'請求
header,
async = true, // 默認爲異步請求
timeout = 60 * 1000, //默認60s
success,
fail,
}) => {
const requestURL = method === 'get' ? this.addURL(url, data) : url;
const sendData = method === 'get' ? null : data;
const xhr = new XMLHttpRequest();
if(header && Object.keys(header).length) {
Object.keys(header).map(key => {
xhr.setRequestHeader(key, header[key]);
})
}
xhr.onreadystatechange = () => {
if(xhr.readyState === 4) {
try {
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
const response = xhr.responseText;
success(response);
} else {
const error = xhr.status + xhr.statusText;
fail(error);
}
} catch (ex) {
}
}
}
xhr.open(method, requestURL, async);
xhr.timeout = timeout;
xhr.ontimeout = () => {
console.log('timeout');
}
xhr.send(sendData);
}
// 拼接url
addURL = (url, param) => {
if(param && Object.keys(param).length) {
url += (url.indexOf('?') === -1 ? '?' : '&');
Object.keys(param).map(key => {
url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
})
}
return url;
}
複製代碼
上述代碼中,利用了try-catch
語句。這是由於當請求在指定時間內沒有返回,就會自動終止,請求終止後,會調用ontimeout
事件處理程序,若是readyState
已經變爲4
,就會調用onreadystatechange
事件處理程序,這種狀況下,會在請求終止後再次訪問status
屬性,就會致使瀏覽器報告錯誤,因此,爲了不錯誤,咱們將status屬性語句封裝在try-catch
語句中。服務器
下面,咱們驗證一下,咱們封裝好的函數是否可用
// html
<button id="ajax_btn">ajax請求</button>
// js
const ajax_btn = document.getElementById('ajax_btn');
ajax_btn.onclik = () => {
ajax({
url: 'http://localhost:3001/123',
data: {},
header: {},
timeout: 20 * 1000,
success: res => {
console.log(res);
},
fail: err => {
throw err;
}
});
};
複製代碼
頁面樣式
三、promise
風格的ajax
請求封裝
promise
風格的代碼
ajax.get('/api').then(res => {}).catch(err => {}).finally();
複製代碼
定義封裝ajax
函數須要的參數
const ajax = ({
url,
data,
method = 'get',
header,
async = true,
timeout = 60 * 1000,
}) => {};
複製代碼
接下來,按照jQuery風格的思路,進行封裝
const ajax = ({
url,
data = {},
method = 'get', // 默認爲'get'請求
header,
async = true, // 默認爲異步請求
timeout = 60 * 1000, //默認60s
}) => {
return new Promise((resolve, reject) => {
const requestURL = method === 'get' ? this.addURL(url, data) : url;
const sendData = method === 'get' ? null : data;
const xhr = new XMLHttpRequest();
if(header && Object.keys(header).length) {
Object.keys(header).map(key => {
xhr.setRequestHeader(key, header[key]);
})
}
xhr.onreadystatechange = () => {
if(xhr.readyState === 4) {
try {
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
const response = xhr.responseText;
resolve(response);
} else {
const error = xhr.status + xhr.statusText;
reject(error);
}
} catch (ex) {
//
}
}
}
xhr.open(method, requestURL, async);
xhr.timeout = timeout;
xhr.ontimeout = () => {
console.log('timeout');
}
xhr.send(sendData);
})
}
// 拼接url
addURL = (url, param) => {
if(param && Object.keys(param).length) {
url += (url.indexOf('?') === -1 ? '?' : '&');
Object.keys(param).map(key => {
url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
})
}
return url;
}
// get請求
ajax.get = (url, data) => {
return ajax({
url,
data,
})
}
// post請求
ajax.post = (url, data) => {
return ajax({
url,
data,
method = 'post',
})
}
複製代碼
下面,咱們驗證一下封裝好的promise
風格的ajax
函數的可用性
// html
<button id="ajax_btn">ajax請求</button>
// js
const ajax_btn = document.getElementById('ajax_btn');
ajax_btn.onclick = () => {
ajax.get('http://localhost:3001/test')
.then(res => {
console.log(res);
})
.catch(err => {
throw err;
})
.finally(console.log('finally'))
}
複製代碼
點擊按鈕
get
請求換成
post
請求
promise
風格的
ajax
請求函數
四、優化
在IE
中,XHR對象是經過MSXML
庫中的ActiveX
對象實現的。因此,在IE
中,可能會有三種不一樣版本的XHR
對象(MSXML2.XMLHttp
、MSXML2.XMLHttp.3.0
、MSXML2.XMLHttp.6.0
),若是咱們要使用庫中的XHR
對象,就須要編寫一個函數(只適用於IE7
之前的版本)
function createXHR() {
if (typeof arguments.callee.activeXString !== 'string') {
const versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
for(let i = 0; i< versions.length; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex) {
// 跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
複製代碼
若是想要支持IE7
以上的版本,只須要在上述函數中加入對原生XHR
對象的支持,即:
function createXHR() {
if (typeof XMLHttpRequest !== 'undefined') {
return new XMLHttpRequest();
} else if(typeof ActiveXObject !== 'undefined') {
if(typeof arguments.callee.activeXString !== 'string') {
const versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
for(let i = 0; i< versions.length; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex) {
// 跳過
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error('no XHR object available');
}
}
複製代碼
經過這樣的方式,咱們能夠直接建立XHR
對象:
const xhr = new createXHR();
複製代碼
效果: