導言
在開始之前先想一想ajax是怎樣的流程
- 首先開啟一個連線
- 傳送資料
- 返回結果
我們要自定義的設定有哪些
- 設定請求方式
- 設定請求頭
- 設定返回資料格式
- 返回成功後或失敗後
我們要做的功能有哪些
- 資料校驗
- 統一資料的格式
- 支援檔案上傳
- 對於傳入引數的容錯處理
- 超時處理 //modify time 2019-01-01
經過以上思考基本結構大致成型
- 資料校驗
- 資料格式的統一
- 建立連線
- 設定請求頭
- 設定返回資料格式
- 傳送資料
- 返回成功或失敗
- 超時處理
程式碼如下
class AJAX {
constructor({url = "",method = "GET",data = {},async = true,success,error,resType = "",headers = {}},time = 5000) {
//集中管理傳遞過來的引數
this.option = {url,method,data,async,success,error,resType,headers,time};
this.timeout;
this.xhr = new XMLHttpRequest();
this.start();
}
start() {
//資料校驗
this.checkOption();
//資料格式的統一
this.initOption();
//建立連線
this.open();
//設定請求頭
this.setHeaders();
//設定返回資料格式
this.setResponseType();
//傳送資料
this.sendData()
//返回成功或失敗
this.responseData();
};
}
複製程式碼
接下來新增校驗功能
- 首先url不能是空
- 然後請求頭必須是字面量物件格式 {key:value}
- 再有就是一些簡單的警告
程式碼如下
checkOption() {
let {url,async,resType,headers,time} = this.option;
if (url === '') {
throw new Error('請求地址不能為空'); //列印錯誤資訊,並停止當前程式
//Console.error('請求地址為空'); 也可以列印錯誤資訊,但是不能停止當前程式
}
if(typeof headers !== 'object'){
throw new Error('設定請求頭時請傳入 {key:value,key:value...} 的格式');
}
if(typeof resType !== 'string'){
throw new Error('設定返回資料格式時請傳入字元出串格式');
}
if(typeof time !== 'number'){
throw new Error('超時時間請傳入數字型別資料');
}
if (typeof url !== 'string') {
//輸出警告資訊
console.warn('當前請求地址不是字串,現在將其嘗試轉換為字串');
}
if (async === false && resType != '') {
console.warn('如果設定了請求方式為同步,即使設定了返回資料格式也不會生效');
}
};
複製程式碼
需要注意的是返回資料格式可以設定這幾個值,之後會寫一個詳細的傳參指南
接下來是資料的處理
- 首先我們需要保證請求格式,不管傳入時是大寫還是小寫,在我們設定請求格式時要是全部大寫
- 還有就是url可能是數字的,需要轉換成字元
- 為了方便將 async不是布林型的轉成布林型,這是什麼概念,就是傳參時 寫數字 1 是非同步 數字 0 是同步
- 將需要傳送的內容做一個處理
initOption() {
let {url,async,method} = this.option;
//url不是字串轉換成字串
if (typeof url !== 'string') {
try {
this.option.url = url.toString();
console.log(`轉換成功: "${this.option.url}"`);
} catch (error) {
throw new Error('url 轉換字串失敗');
}
}
//async不是布林型轉成布林型
if(typeof async !=='boolean'){
async == true ? this.option.async = true : this.option.async = false;
}
//將 post get 轉換為大寫
this.option.method = method.toUpperCase();
//post和get資料初始化
if(this.option.method != 'FORMDATA'){// [1]
let data = this.option.data;
if(typeof data === 'object'){//[2]
if( this.option.method === 'GET'){
let arr=[];
for(let name in data){
arr.push(`${name}=${data[name]}`);//[3]
}
let strData=arr.join('&');//[4]
this.option.data=`?${strData}`;//[5]
}else if( this.option.method === 'POST'){
let formData = new FormData();//[6]
for(let key in data){
formData.append(`${key}`,`${data[key]}`);
}
this.option.data=formData;
}
}else if(typeof data === 'string' && this.option.method === 'GET'){//[7]
this.option.data=`?${data}`;
}
}
};
複製程式碼
這裡詳細說說對需要傳送資料的處理,按照序號來說
- 判斷它不是 formData ,也就是說是 GET 和 POST 時我們進行資料處理,是 formData 不進行處理,直接傳送,這是為了能夠實現檔案上傳功能
- 判斷它是不是 {key:vlue} 這種格式的,是的話解析或拼接,不是的話跳到 [7] 如果是字串直接加到 url 後邊
- [3] [4] [5] 這裡是為了處理成 url?key=value$key=value 這種 url 傳參的資料格式
- [6] 是新建了一個 FormData 物件,是 ajax2.0 裡邊的,它最主要的可以用 ajax 實現檔案上傳功能,在這裡是為了程式碼簡單
開啟連線
經過之前的資料處理這裡只需要判斷下是 GET 還是其他方式(post formdata),然後選擇對應的連線方式
open(){
let {method,url,async,data} = this.option;
if(method === 'GET'){
this.xhr.open(method,url+data,async);
}else{
this.xhr.open(method,url,async);
}
}
複製程式碼
設定自定義請求頭
將傳入的引數進行解析,然後設定自定義請求頭 程式碼如下
setHeaders(){
let headers = this.option.headers;
for(let key in headers){
this.xhr.setRequestHeader(`${key.toString()}`,`${headers[key].toString()}`)
}
}
複製程式碼
設定返回資料格式、傳送資料
- 由於同步請求時不能設定返回資料格式,所以做下判斷
- 傳送資料這裡,在經過之前的資料處理後只有 GET 方式有所區別,其他兩種沒有區別(支援 GET POST 以及我自己定義的一種,更多請求方法可自行擴充套件)
setResponseType() {
if (this.option.async) {
this.xhr.responseType = this.option.resType;
}
}
複製程式碼
新增超時處理
sendData(){
if(this.option.method == 'GET'){
this.xhr.send();
}else{
this.xhr.send(this.option.data);
}
this.timeout = setTimeout(()=>{
typeof this.option.error === 'function' && this.option.error('請求超時,預設超時時間為 5000 毫秒');
this.option.reject('請求超時,預設超時時間為 5000 毫秒');
}, this.option.time);
}
複製程式碼
請求完成後的資料返回
- 請求完成後會返回資料
判斷 success 以及 error 是不是函式,是的話會將資料返回給 success 或者將錯誤資訊返回給 error
responseData(){
this.xhr.onload = ()=>{
if(this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status === 304){
typeof this.option.success === 'function' && this.option.success(this.xhr.response);
}else{
typeof this.option.error === 'function' && this.option.error(this.xhr.statusText);
}
}
}
複製程式碼
在實現基本功能後,突然想到 jQuery 的 ajax 是會返回一個 promise 物件,可以同時使用回掉函式,或者使用 then 和 catch 來處理資料
因此修改了下傳入引數,以及返回資料的處理
傳參時程式碼如下
//add resolve reject
class AJAX {
constructor({url = "",method = "GET",data = {},async = true,success,error,resType = "",headers = {},resolve,reject}) {
this.option = {url,method,data,async,success,error,resType,headers,resolve,reject};
this.xhr = new XMLHttpRequest();
this.start();
}
}
複製程式碼
返回資料時程式碼如下
responseData(){
this.xhr.onload = ()=>{
if(this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status === 304){
clearTimeout(this.timeout);
typeof this.option.success === 'function' && this.option.success(this.xhr.response);
this.option.resolve(this.xhr.response);//add
}else{
clearTimeout(this.timeout);
typeof this.option.error === 'function' && this.option.error(this.xhr.statusText);
this.option.reject(this.xhr.statusText);//add
}
}
}
複製程式碼
最終程式碼
class AJAX {
constructor({url = "",method = "GET",data = {},async = true,success,error,resType = "",headers = {},resolve,reject}) {
this.option = {url,method,data,async,success,error,resType,headers,resolve,reject};
this.xhr = new XMLHttpRequest();
this.start();
}
start() {
//資料校驗
this.checkOption();
//資料格式的統一
this.initOption();
//建立連線
this.open();
//設定請求頭
this.setHeaders();
//設定返回資料格式
this.setResponseType();
//傳送資料
this.sendData()
//返回成功或失敗
this.responseData();
};
checkOption() {
let {url,async,resType,headers} = this.option;
if (url === '') {
throw new Error('請求地址不能為空'); //列印錯誤資訊,並停止當前程式
//Console.error('請求地址為空'); 也可以列印錯誤資訊,但是不能停止當前程式
}
if(typeof headers !== 'object'){
throw new Error('設定請求頭時請傳入 {key:value,key:value...} 的格式');
}
if(typeof time !== 'number'){
throw new Error('超時時間請傳入數字型別資料');
}
if(typeof resType !== 'string'){
throw new Error('設定返回資料格式時請傳入字元出串格式');
}
// "" 與設定為"text"相同, 是預設型別 (實際上是 DOMString)
// "arraybuffer" 將接收到的資料型別視為一個包含二進位制資料的 JavaScript ArrayBuffer
// "blob" 將接收到的資料型別視為一個包含二進位制資料的 Blob 物件
// "document" 將接收到的資料型別視為一個 HTML Document 或 XML XMLDocument ,這取決於接收到的資料的 MIME 型別
// "json" 將接收到的資料型別視為 JSON 解析得到的
// "text" 將接收到的資料型別視為包含在 DOMString 物件中的文字
if (typeof url !== 'string') {
//輸出警告資訊
console.warn('當前請求地址不是字串,現在將其嘗試轉換為字串');
}
if (async === false && resType != '') {
console.warn('如果設定了請求方式為同步,即使設定了返回資料格式也不會生效');
}
};
initOption() {
let {url,async,method} = this.option;
//url不是字串轉換成字串
if (typeof url !== 'string') {
try {
this.option.url = url.toString();
console.log(`轉換成功: "${this.option.url}"`);
} catch (error) {
throw new Error('url 轉換字串失敗');
}
}
//async不是布林型轉成布林型
if(typeof async !=='boolean'){
async == true ? this.option.async = true : this.option.async = false;
}
//將 post get 轉換為大寫
this.option.method = method.toUpperCase();
//post和get資料初始化
if(this.option.method != 'FORMDATA'){
let data = this.option.data;
if(typeof data === 'object'){
if( this.option.method === 'GET'){
let arr=[];
for(let name in data){
arr.push(`${name}=${data[name]}`);
}
let strData=arr.join('&');
this.option.data=`?${strData}`;
}else if( this.option.method === 'POST'){
let formData = new FormData();
for(let key in data){
formData.append(`${key}`,`${data[key]}`);
}
this.option.data=formData;
}
}else if(typeof data === 'string' && this.option.method === 'GET'){
this.option.data=`?${data}`;
}
}
};
open(){
let {method,url,async,data} = this.option;
if(method === 'GET'){
this.xhr.open(method,url+data,async);
}else{
this.xhr.open(method,url,async);
}
}
setHeaders(){
let headers = this.option.headers;
for(let key in headers){
this.xhr.setRequestHeader(`${key.toString()}`,`${headers[key].toString()}`)
}
}
setResponseType() {
if (this.option.async) {
this.xhr.responseType = this.option.resType;
}
}
sendData(){
if(this.option.method == 'GET'){
this.xhr.send();
}else{
this.xhr.send(this.option.data);
}
this.timeout = setTimeout(()=>{
typeof this.option.error === 'function' && this.option.error('請求超時,預設超時時間為 5000 毫秒');
this.option.reject('請求超時,預設超時時間為 5000 毫秒');
}, this.option.time);
}
responseData(){
this.xhr.onload = ()=>{
if(this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status === 304){
clearTimeout(this.timeout);
typeof this.option.success === 'function' && this.option.success(this.xhr.response);
this.option.resolve(this.xhr.response);//add
}else{
clearTimeout(this.timeout);
typeof this.option.error === 'function' && this.option.error(this.xhr.statusText);
this.option.reject(this.xhr.statusText);//add
}
}
}
all(promises) {
return Promise.all(promises);
};
}
function ajax({url,method,data,async,success,error,resType,headers,time}){
return new Promise((resolve, reject) => {
return new AJAX({url,method,data,async,success,error,resType,headers,time,resolve,reject});
});
}
複製程式碼
使用時可以將程式碼複製貼上到單獨的 js 檔案然後用 script 標籤引入
也可以新增一行 export 程式碼將最後的 ajax 暴露出去 使用import 引入
具體使用方法用法
ajax({
url:'api/login',
method:'post',//支援 GET POST 和我自定義的 FORMDATA ,傳入時不區分大小寫
data = {
name:"yhtx",
id:"1997"
},//除了這種還支援字串 "name=yhtx&id=1997";以及 formData 資料,在傳入formData 資料時請將 method 設定為 FORMDATA
async = true,//可以使用數字 1 代替 true ,數字 0 代替 false
time = 5000,//請求超時時間,預設為 5000 毫秒
success(res){
//可以使用回撥的形式處理資料也可以使用 then
},error(err){
//可以使用回撥的形式處理錯誤也可以使用 catch
},
resType = "",//可以傳入 "" "arraybuffer" "blob" "document" "json" "text"
headers = {
mycookie: "46afqwiocibQEIJfa498./&678" //使用物件的方式傳參
}
}).then((res)=>{
//可以使用 then 的形式處理資料也可以使用回撥函式
}).catch((err)=>{
//可以使用 catch 的形式處理資料也可以使用回撥函式
})
複製程式碼