什么是ajax
●認識前后端交互
????○就是 前端 與 后端的 一種通訊方式
????○主要使用的技術棧就是 ajax (async javascript and xml)
●ajax 特點
????○使用 ajax 技術網頁應用能夠快速的將新內容呈現在用戶界面
????○并且不需要刷新整個頁面, 也就是能夠讓頁面有 "無刷更新" 的效果
●注意點:
????○前后端交互只能交互 字符串
????○并且有自己的固定步驟
創建ajax 基本步驟的是什么
1.創建 ajax 對象
const xhr = new XMLHttpRequest()
2.配置 ajax 對象
// xhr.open('請求的方式', '請求的地址', 是否異步)
xhr.open('GET', 'http://localhost:8888/test/first', true)
3.發送請求
xhr.send()
4.接收響應???????
xhr.onload = function () {
console.log('請求回來了~~~~~~~~')
console.log(xhr.responseText)
}
ajax 狀態碼有哪些
●簡單來說其實就是用一個數字表明了當前 ajax 運行到哪一步了
●語法: xhr.readyState
????○0: 創建 ajax 成功
????○1: 當前 ajax 配置成功
????○2: 當前 ajax 發送成功(響應已經回到瀏覽器了)
????○3: 表示瀏覽器當前正在解析本次響應, 但可能還沒完成
????○4: 表示瀏覽器已經完成解析本次響應, 可以正常使用 responseText 了
●0 和 1, 比較好打印, 2/3/4 這幾個我們可以借助一個 事件去打印
????○readyStatechange 事件
????○通過事件名其實就可以看出, 當 readyState 發生改變時就會執行???????
const xhr = new XMLHttpRequest()
console.log(xhr.readyState) // 0
xhr.open('GET', 'http://localhost:8888/test/first', true)
console.log(xhr.readyState) // 1
xhr.onreadystatechange = function () {
if (xhr.readyState === 2) console.log(xhr.responseText)
if (xhr.readyState === 3) console.log(xhr.responseText)
if (xhr.readyState === 4) console.log(xhr.responseText)
}
xhr.send()
ajax 常見請求方式有哪些
● GET
????○表示向服務器獲取資源
●POST
????○表示向服務器提交信息,通常用于產生新的數據,比如注冊
●PUT
????○表示希望修改服務器的數據, 通常用于修改某數據
●DELETE
????○表示希望刪除服務器的數據
●OPTIONS
????○發生在跨域的預檢請求中,表示客戶端向服務器申請跨域提交
ajax 中 get 和 post 請求攜帶參數的方式
●GET: 直接拼接在請求路徑后, 以 ? 間隔, 使用 key=value 的形式書寫, 當有多個參數的時候用 & 連接???????
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8888/test/third?name=QF666&age=18')
xhr.send()
xhr.onload = function () {
let res = JSON.parse(xhr.responseText)
console.log(res)
}
●POST
????○在請求體內攜帶參數(其實就是 send 小括號內部)
????○并且需要設置請求頭內部的 content-type
????????■如果參數為 查詢字符串, 需要添加:
????????????●'xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')'
????????■如果參數為 JSON 字符串, 需要添加:
????????????●'xhr.setRequestHeader('content-type', 'application/json')'???????
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:8888/test/fourth')
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
xhr.send('name=QF666&age=18')
xhr.onload = function () {
let res = JSON.parse(xhr.responseText)
console.log(res)
}
ajax 中 get 和 post 的區別有哪些
????1.攜帶參數的位置
????????a.GET: 直接在地址后面書寫
????????b.POST: 在請求體內書寫
????2.攜帶參數的大小
????????a.GET: 2kb(左右)
????????b.POST: 原則上不限制大小, 但是服務器可以做出限制
????3.攜帶參數的格式
????????a.GET: 只能攜帶查詢字符串格式
????????b.POST: 原則上不限制格式, 但是需要在請求報文的 content-type 做出配置
????4.安全性(相對)
????????a.GET: 明文發送, 相對不安全
????????b.POST: 暗文發送, 相對安全
●http 傳輸協議
????○http(s) 協議規定了, 只能由前端主動發起
????○并且在傳輸的過程中, 只能傳遞 字符串
●http 協議過程
????1.建立連接
????????瀏覽器和服務器進行連接建立
????????基于 TCP/IP 協議的三次握手
????2.發送請求
????????要求前端必須以 請求報文 的形式發送
????????報文由瀏覽器組裝, 我們只需要提供對應的信息即可
????????報文包含的內容
???????????? 請求報文行
?????????????請求方式, 請求地址, 傳輸協議
?????????????請求報文頭(對本次請求的一些說明信息)
?????????????userAgent: 請求方終端信息
?????????????accept: 期望后端返回的數據類型
?????????????content-type: 請求攜帶的 "材料" 的數據格式
?????????????cookie: 只要 cookie 空間內有內容, 會自動攜帶
????????請求報文空行
????????請求報文體(不是所有請求都有)
????3.? 接收響應
? ? ? ??要求后端必須以響應報文的形式返回
? ? ? ? 報文由服務器組裝
????????響應報文包含的內容
? ? ? ? ? ? ? ? 響應報文行
? ? ? ? ? ? ? ? 響應狀態碼, 簡單信息描述響應狀態碼, 傳輸協議
? ? ? ? ?響應報文頭(對本次響應的一些說明信息)
????????????????server: 哪一個服務器給你返回的信息
? ? ? ? ? ? ? ? date: 時間, 服務器時間
? ? ? ? ? ? ? ? content-length: 響應體長度
? ? ? ? ? ? ? ? content-type: 響應數據類型
????????????????響應報文體(后端返回給前端的一些信息)
????4.? 斷開連接
????????? 瀏覽器和服務器斷開連接
? ? ? ? ?基于 TCP/IP 協議的四次揮手
●?響應狀態碼
????○?100~199 表示連接繼續
????○?200~299 表示各種成功
????○?300~399 表示重定向
????○?400~499 表示各種客戶端錯誤
????○?500~599 表示各種服務端錯誤
●?回調函數
????○?把函數 A 以實參的形式傳遞到 函數 B 內
????○?在函數 B 內以形參的方式調用到 函數 A
????○?此時我們可以把函數 A 叫做函數 B 的 回調函數
????○?我們在封裝異步代碼的時候會用到回調函數???????
function fnA () {console.log('我是 fnA 函數內部的代碼')}
function fnB(callback) {callback()}
fnB(fnA)
●?使用回調函數封裝一個異步代碼???????
function fn(jinnang = () => {}) {
console.log("班長去買水了");
const timer = Math.ceil(Math.random() * 3000);
setTimeout(() => {
console.log("班長買完水了");
console.log("耗時", timer);
console.log("按照錦囊內的內容行事");
jinnang();
}, timer);
}
/**
* fn 函數一旦調用, 班長出發開始去買水
* 在班長出發的時候, 給他一個錦囊
*/
fn(() => {
console.log("去買一瓶牛奶");
});
fn();
●?上述代碼已經完成了一個異步的封裝
●?不過在工作中我們更多的是封裝網絡請求這種異步代碼
●?但是我們這里通過一個 '買水耗時' 來模擬一個網絡請求的延遲, 我們約定如果時間超過 3500 毫秒, 那么就算是失敗, 否則就是成功???????
function fn(jinnang = () => {}) {
console.log("班長去買水了");
const timer = Math.ceil(Math.random() * 3000) + 2000;
setTimeout(() => {
if (timer > 3500) {
console.log("請求失敗", timer);
} else {
console.log("請求成功", timer);
}
}, timer);
}
/**
* fn 函數一旦調用, 班長出發開始去買水
* 在班長出發的時候, 給他一個錦囊
*/
fn(() => {
console.log("去買一瓶牛奶");
});
fn();
●?此時我們已經封裝完畢了, 只不過這種封裝方式會帶來另一個問題, 就是回調地獄
●?回調地獄: 當你使用回調函數過多的時候, 會出現的一種代碼書寫結構
●?需求:
????○?再買水成功后, 讓班長幫忙退掉
????○?在推掉以后, 再次讓班長幫忙買水 (此時必須要在前一瓶水購買完成之后再去購買)
????○?在第二次買水成功以后, 再次讓班長去買水???????
fn(
() => {
console.log("班長第一次買水成功, 幫我退掉");
fn(
() => {
console.log("班長第二次買水成功");
fn(
() => {
console.log("班長第三次買水成功");
},
() => {
console.log("班長第三次買水失敗");
}
);
},
() => {
console.log("班長第二次買水失敗");
}
);
},
() => {
console.log("班長第一次買水失敗");
}
);
●這段代碼運行沒有任何問題, 但是閱讀起來極其不利于理解
????○原因:
????????■按照回調函數的語法進行封裝, 只能通過傳遞一個函數作為參數來調用
????????■當你使用回調函數過多的時候, 會出現回調地獄的代碼結構
????○解決:
????????■不按照回調函數的語法封裝
????????■ES6 推出了一種新的封裝異步代碼的方式, 叫做 Promise (承諾, 期約)
Promise
是一種異步代碼的封裝方案
因為換了一種封裝方案, 不需要安裝回調函數的方式去調用, 需要按照 promise 的形式去調用
注意 promise 不是解決 異步問題的, 而是解決回調地獄問題的
●認識 Promise
????○promise 的三種狀態
????????■持續: pending
????????■成功: fulfilled
????????■失敗: rejected
????○promise 的兩種轉換
????????■從持續轉為成功
????????■從持續轉為失敗
????○promise 的基礎語法
????????■ES6 內置構造函數
????○promise 的語法
????????■const p = new Promise(function () {})
????○promise 對象可以觸發的兩個方法
????????■p.then(函數); 成功時執行
????????■p.catch(函數); 失敗時執行
●promise 封裝一個異步函數
???????
const p = new Promise(function (resolve, reject) {
// resolve: 是一個形參, 名字自定義, 值是一個函數, 當你調用的時候, 會把當前 promise 的狀態轉換為 成功
// reject: 是一個形參, 名字自定義, 值是一個函數, 當你調用的時候, 會把當前 promise 的狀態轉換為 失敗
// resolve 和 reject 調用時可以傳遞一個參數, 這個參數會被傳遞給對應的 then catch
const timer = Math.ceil(Math.random() * 3000) + 2000;
setTimeout(() => {
if (timer > 3500) {
console.log("買水失敗, 耗時 ", timer);
reject("獎勵一個bug");
} else {
console.log("買水成功, 耗時: ", timer);
resolve("送你十個bug");
}
}, timer);
});
p.then(function (address) {
console.log("班長買水成功咯~~~", address);
});
p.catch(function (address) {
console.log("班長買水失敗咯~~~", address);
});
● 封裝 promise 為函數???????
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000) + 2000;
setTimeout(() => {
if (timer > 3500) {
reject("班長買水失敗");
} else {
resolve("班長買水成功");
}
}, timer);
});
return p;
}
// 將來在使用的時候 res 得到的是 promise 的實例對象 p
const res = fn();
res.then(function (type) {
// 這個函數執行代碼 promise 狀態為成功狀態!!!
console.log("因為", type, "謝謝班長, 我準備了20個bug, 回饋給你");
});
res.catch(function (type) {
// 這個函數執行代碼
console.log("因為", type, "謝謝班長, 我準備了800個bug, 開心死你");
});
●promise 的鏈式調用
???????
fn()
.then(function (type) {
// 這個函數執行代碼 promise 狀態為成功狀態!!!
console.log("因為", type, "謝謝班長, 我準備了20個bug, 回饋給你");
})
.catch(function (type) {
// 這個函數執行代碼
console.log("因為", type, "謝謝班長, 我準備了800個bug, 開心死你");
});
●promise 的調用方式補充
????○如果你在第一個 then 里面返回(return) 一個新的 promise 對象的時候
????○可以在第一個 then 后面, 繼續第二個 then???????
fn()
.then(function (type) {
console.log(
"第一次: 因為",
type,
"謝謝班長, 我準備了20個bug, 回饋給你"
);
return fn();
})
.then(function (type) {
console.log(
"第二次: 因為",
type,
"謝謝班長, 我準備了20個bug, 回饋給你"
);
return fn();
})
.then(function (type) {
console.log(
"第三次: 因為",
type,
"謝謝班長, 我準備了20個bug, 回饋給你"
);
return fn();
})
.catch(function (type) {
console.log("因為", type, "謝謝班長, 我準備了800個bug, 開心死你");
});
●promise 的其他方法
●Promise 實例的 finally 方法
????○不管promise是成功還是失敗, 只要 promise 執行結束, 我都會執行???????
fn()
.then(function (res) {
console.log("成功");
})
.catch(function (res) {
console.log("失敗");
})
.finally(function () {
console.log(
"不管promise是成功還是失敗, 只要 promise 執行結束, 我都會執行"
);
});
● Promise 本身還有一些方法
????○all:
????????■作用: 可以同時觸發多個 promise 行為
????????????●只有所有的 promise 都成功的時候, all 才算成功
????????????●只要任何一個 promise 失敗的時候, all 就算失敗了
????????■語法: Promise.all([多個 promise])
○race:
????????■作用: 可以同時觸發多個 promise 行為
????????????●按照速度計算, 當第一個結束的時候就結束了, 成功或失敗取決于第一個執行結束的 promise
????????■語法: Promise.race([多個 promise])
○allSettled
????????■作用: 可以同時觸發多個 Promise 行為
????????????●不管多個成功還是失敗都會觸發
????????????●會在結果內以數組的形式給你返回 每一個 promise 行為的成功還是失敗
????????■語法: Promise.allSettled([多個 promise])
○resolve
????????■作用: 強制返回一個成功狀態的 promise 對象
○reject
????????■作用: 強制返回一個失敗狀態的 promise 對象
???????
// 1. all
Promise.all([fn(), fn(), fn()])
.then(function () {
console.log("所有的 參數 都返回 成功狀態");
})
.catch(function () {
console.log("這些參數中, 有一個 為 失敗狀態");
});
// 2. race
Promise.race([fn(), fn(), fn()])
.then(function () {
console.log("速度最快的那個執行完畢, 并且是成功狀態時 執行");
})
.catch(function () {
console.log("速度最快的那個執行完畢, 并且是失敗狀態時 執行");
});
// 3. allSettled
Promise.allSettled([fn(), fn(), fn()])
.then(function (res) {
console.log(res);
})
.catch(function (res) {
console.log(res);
});
// 4. resolve
Promise.resolve()
.then(function (res) {
console.log("成功");
})
.catch(function (res) {
console.log("失敗");
});
// 5. reject
Promise.reject()
.then(function (res) {
console.log("成功");
})
.catch(function (res) {
console.log("失敗");
});
●async / await
上述我們已經把 promise 的基礎使用掌握了, 但是個人認為, promise 的鏈式調用仍然會有點小問題
就是在使用的時候, 過多的鏈式調用, 對于閱讀體驗來說, 仍然是有一點小問題, 不利于閱讀
所以我們可以 使用 ES6+ 新推出的 async與await, 使用我的異步代碼書寫的更像是同步代碼一樣
●注意: 需要配合的必須是 Promise
●async 關鍵字的用法:
????○直接書寫在函數的前面即可, 表示該函數是一個異步函數
????○意義: 表示在該函數內部可以使用 await 關鍵字
●await 關鍵字的用法:
????○必須書寫在一個有 async 關鍵字的函數內
????○await 后面等待的內容必須是一個 promise 對象
????○本該使用 then 接受的結果, 可以直接定義變量接受了
●常規的 promise 調用方式
???????
fn()
.then(function (res) {
console.log(res);
})
.catch(function (res) {
console.log(res);
});
●利用 async 和 await 關鍵字來使用
???????
async function newFn() {
/**
* await 是等待的意思
*
* 在當前 fn 函數內, await 必須要等到后面的 Promise 結束以后, 才會繼續執行后續代碼
*/
const r1 = await fn();
console.log("第一次: ", r1);
const r2 = await fn();
console.log("第二次: ", r1);
const r3 = await fn();
console.log("第三次: ", r1);
}
newFn();
●async 和 await 語法的缺點
????○await 只能捕獲到 Promise 成功的狀態
????○如果失敗, 會報錯并且終止程序的繼續執行???????
async function newFu() {
const r1 = await fn();
console.log("失敗后, 提示用戶網絡錯誤"); // 如果失敗的話這行代碼并不會執行
}
newFu();
● 解決方法1: 使用 try...catch...
????○語法: try { 執行代碼 } catch (err) { 執行的代碼 }
????○首先執行 try 內部的代碼, 如果不報錯, catch 的代碼不執行了
????○如果報錯, 不會爆出錯誤, 不會終止程序, 而是執行 catch 的代碼, 報錯信息在 catch 函數的形參內
???????
async function newFu() {
try {
const r1 = await fn();
console.log(r1);
} catch (error) {
console.log("網絡錯誤, 請檢查網絡并重新請求");
}
}
newFu();
●解決方法2: 改變封裝的思路
????○原因: 因為 promise 對象有成功和失敗的狀態, 所以會在失敗狀態是報錯
????○解決: 封裝一個 百分比成功的 promise 對象, 讓成功和失敗的時候都按照 resolve 的形式來執行
????○只不過傳遞出去的參數, 記錄一個表示成功或者失敗的信息
???????
function fn() {
const p = new Promise(function (resolve, reject) {
const timer = Math.ceil(Math.random() * 3000) + 2000;
setTimeout(() => {
if (timer > 3500) {
resolve({ code: 0, msg: "班長買水失敗" });
} else {
resolve({ code: 1, msg: "班長買水成功" });
}
}, timer);
});
return p;
}
async function newFn() {
const r1 = await fn();
if (r1.code === 0) {
console.log("第一次請求失敗, 請檢查您的網絡信息");
} else {
console.log("第一次請求成功", r1.msg);
}
const r2 = await fn();
if (r2.code == 0) {
console.log("第二次請求失敗, 請檢查您的網絡信息");
} else {
console.log("第二次請求成功", r2.msg);
}
}
newFn();
●封裝 ajax
1.ajax 封裝解析
????a.封裝方案
????????i.選擇回調函數
????????????1.將來使用的時候需要按照回調函數的語法使用
????????????2.但是容易出現回調地獄
????????ii.選擇 promise 的形式
????????????1.按照 Promise 的形式來使用
????????????2.后續可以利用 async/await 語法進一步簡化代碼
????b.決定參數
????????i. 請求地址(url): 必填
????????ii.請求方式(method): 選填, 默認 GET
????????iii.是否異步(async): 選填, 默認 true 異步
????????iv.攜帶的參數(data): 選填, 默認 '' 空字符
????c.決定返回值
????????i.需要
????????ii.返回一個 promise 對象
????????????1.因為需要依賴返回值來決定使用 then 還是 catch
????d.參數書寫順序
????????i.因為有多個參數, 并且有些參數可以有默認值
????????ii.所以我們只能通過傳遞一個對象的方式去處理
function ajax(options) {}
2.ajax 封裝參數驗證????????????????
function ajax(options = {}) {
// 1.1 驗證參數---url 必傳
if (options.url == undefined) throw new Error("url 為必填項, 請填寫后再試");
// 1.2 驗證參數---method 可以不傳, 可以為 GET, 可以是 POST, 但不能是其他的
if (
!(options.method === undefined || /^(GET|POST)$/i.test(options.method))
) {
throw new Error("mehtod 目前僅支持 GET 或 POST");
}
// 1.3 驗證參數---async 可以不傳, 可以為 true, 可以是 false, 但不能是其他的
if (!(options.async === undefined || typeof options.async === "boolean")) {
throw new Error("async 目前僅支持 布爾值(Boolean)");
}
// 1.4 驗證參數---data 可以不傳, 可以為一個字符串, 可以是一個對象
if (
!(
options.data === undefined ||
typeof options.data === "string" ||
options.data.constructor === Object
)
) {
throw new Error("data 目前僅支持 字符串(String) 或 對象(Object)");
}
// console.log('這里執行說明參數的 url 已經傳遞了')
}
ajax({
url: "qwer",
// method: 'POST',
// async: false,
data: "name=QF666&age=18",
});
3.ajax 封裝默認值???????
function objToStr(obj) {
let str = "";
for (let k in obj) {
str += `${k}=${obj[k]}&`;
}
return str.slice(0, str.length - 1);
}
function ajax(options = {}) {
// 1 驗證參數---代碼省略
// 2.1 處理默認值
const _options = {
url: options.url,
// 代碼執行到這里, method 要么是 undefined 要么是 GET或者POST
method: options.method || "GET",
// 代碼執行到這里, async 要么是 undefined 要么是 true 或者 false
// ?? 空值運算符, 只有在 左側為 undefined 或者 null 時才會執行右邊的
async: options.async ?? true,
// 代碼執行到這里, data 要么是 undefined 要么是 '' 或者 {}
data: options.data || "",
};
// 2.2 如果當前 data 是對象, 需要轉換為 查詢字符串
if (typeof _options.data !== "string") {
_options.data = objToStr(_options.data);
}
// 2.3 如果當前 data 有值, 且當前是 GET 方式, 那么可以提前 把 url 后拼接上 data
if (_options.data && /^(GET)$/i.test(_options.method)) {
_options.url += "?" + _options.data;
}
console.log("原始參數: ", options);
console.log("默認參數: ", _options);
// console.log('這里執行說明參數的 url 已經傳遞了')
}
4.ajax 封裝發送請求???????
function ajax(options = {}) {
// 1 驗證參數---代碼省略
// 2 處理默認參數---代碼省略
// 3. 封裝 ajax 請求
const p = new Promise((resolve, reject) => {
// 3.1 創建 ajax
const xhr = new XMLHttpRequest();
// 3.2 配置 ajax 請求信息
xhr.open(_options.method, _options.url, _options.async);
// 3.3 配置接收響應的事件
xhr.onload = function () {
resolve(xhr.responseText);
};
// 3.4 發送本次請求
xhr.send();
});
return p;
}
ajax({
url: "http://localhost:8888/test/third",
// method: "POST",
async: false,
data: "name=QF666&age=18",
// data: {
// name: "QF666",
// age: 18,
// abc: 123,
// },
}).then((res) => {
console.log(res);
});
5.ajax 封裝請求頭信息???????
function ajax(options = {}) {
// 1 驗證參數---部分代碼省略
// 1.5 驗證參數---headers 可以不傳, 可以為一個對象
if (
!(
options.headers === undefined ||
options.headers.constructor === Object
)
) {
throw new Error("herder 目前僅支持對象(Object)");
}
// 2.1 處理默認值
const _options = {
// code...
// 代碼執行到這里, headers 要么是 undefined 要么是 {}
headers: {
"content-type": "application/x-www-form-urlencoded",
...options.headers,
},
};
// 3. 封裝 ajax 請求
const p = new Promise((resolve, reject) => {
// 3.1 創建 ajax
// 3.2 配置 ajax 請求信息
// 3.3 配置接收響應的事件
// 如果當前的請求方式為 POST, 那么需要配置上對應的 請求頭
if (/^(POST)$/i.test(_options.method)) {
xhr.setRequestHeader(
"content-type",
_options.headers["content-type"]
);
}
// 如果 authorization 內有值, 需要帶上 authorization
if (_options.headers.authorization) {
xhr.setRequestHeader(
"authorization",
_options.headers.authorization
);
}
// 3.4 發送本次請求
/^(POST)$/i.test(_options.method)
? xhr.send(_options.data)
: xhr.send();
});
return p;
}
/**
* 思考: 請求頭的設置
* + 當你是 post 的時候, 需要設置 content-type 字段
* + 有的時候還需要設置 authorization 字段
* + 有的時候還需要設置 abcd 字段
*
* 例子:
* 1. 登錄
* + POST 方式
* => 需要 content-type
* => 不需要 authorization
* 2. 獲取商品列表
* + GET 方式
* => 不需要 content-type
* => 不需要 authorization
* 3. 獲取購物車列表
* + GET 方式
* => 不需要設置 content-type
* => 需要 authorization
* 4. 修改密碼
* + POST 方式
* => 需要 content-type
* => 需要 authorization
*/
ajax({
url: "http://localhost:8888/test/third",
// method: "POST",
// async: false,
// data: "name=QF666&age=18",
data: {
name: "QF666",
age: 18,
abc: 123,
},
headers: { authorization: "123" },
}).then((res) => {
console.log(res);
});
6.ajax 封裝解析參數???????
function ajax(options = {}) {
// 1.6 驗證參數---dataType 可以不傳, 可以為 'string' 可以為 'json'
if (
!(
options.dataType === undefined ||
/^(string|json)$/i.test(options.dataType)
)
) {
throw new Error("dataType 目前僅支持 'string' 或者 'json' ");
}
// 2.1 處理默認值
const _options = {
// 代碼執行到這里, dataType 要么是 undefined 要么是 string 要么是 json
dataType: options.dataType || "string",
};
// 3. 封裝 ajax 請求
const p = new Promise((resolve, reject) => {
// 3.3 配置接收響應的事件
xhr.onload = function () {
if (_options.dataType === "string") {
resolve({
code: 1,
info: xhr.responseText,
msg: "成功",
});
return;
}
try {
const res = JSON.parse(xhr.responseText);
resolve({
code: 1,
info: res,
msg: "成功",
});
} catch (error) {
resolve({
code: 0,
info: error,
msg: "失敗",
});
}
};
});
return p;
}
7.ajax 封裝基準地址???????
function outer(url) {
let baseUrl = url;
function ajax(options = {}) {
// 2.1 處理默認值
const _options = {
url: baseUrl + options.url,
};
// 省略部分代碼...
}
return ajax;
}
const res = outer("http://localhost:8888");
res({
url: "/test/first",
}).then((res) => {
console.log(res);
});
res({
url: "/test/second",
dataType: "json",
}).then((res) => {
console.log(res);
});
8.完整版 ajax 封裝代碼???????
function objToStr(obj) {
let str = "";
for (let k in obj) {
str += `${k}=${obj[k]}&`;
}
return str.slice(0, str.length - 1);
}
function outer(url) {
let baseUrl = url;
function ajax(options = {}) {
// 1.1 驗證參數---url 必傳
if (options.url == undefined)
throw new Error("url 為必填項, 請填寫后再試");
// 1.2 驗證參數---method 可以不傳, 可以為 GET, 可以是 POST, 但不能是其他的
if (
!(
options.method === undefined ||
/^(GET|POST)$/i.test(options.method)
)
) {
throw new Error("mehtod 目前僅支持 GET 或 POST");
}
// 1.3 驗證參數---async 可以不傳, 可以為 true, 可以是 false, 但不能是其他的
if (
!(options.async === undefined || typeof options.async === "boolean")
) {
throw new Error("async 目前僅支持 布爾值(Boolean)");
}
// 1.4 驗證參數---data 可以不傳, 可以為一個字符串, 可以是一個對象
if (
!(
options.data === undefined ||
typeof options.data === "string" ||
options.data.constructor === Object
)
) {
throw new Error("data 目前僅支持 字符串(String) 或 對象(Object)");
}
// 1.5 驗證參數---headers 可以不傳, 可以為一個對象
if (
!(
options.headers === undefined ||
options.headers.constructor === Object
)
) {
throw new Error("herder 目前僅支持對象(Object)");
}
// 1.6 驗證參數---dataType 可以不傳, 可以為 'string' 可以為 'json'
if (
!(
options.dataType === undefined ||
/^(string|json)$/i.test(options.dataType)
)
) {
throw new Error("dataType 目前僅支持 'string' 或者 'json' ");
}
// 2.1 處理默認值
const _options = {
url: baseUrl + options.url,
// 代碼執行到這里, method 要么是 undefined 要么是 GET或者POST
method: options.method || "GET",
// 代碼執行到這里, async 要么是 undefined 要么是 true 或者 false
// ?? 空值運算符, 只有在 左側為 undefined 或者 null 時才會執行右邊的
async: options.async ?? true,
// 代碼執行到這里, data 要么是 undefined 要么是 '' 或者 {}
data: options.data || "",
// 代碼執行到這里, headers 要么是 undefined 要么是 {}
headers: {
"content-type": "application/x-www-form-urlencoded",
...options.headers,
},
// 代碼執行到這里, dataType 要么是 undefined 要么是 string 要么是 json
dataType: options.dataType || "string",
};
// 2.2 如果當前 data 是對象, 需要轉換為 查詢字符串
if (typeof _options.data !== "string") {
_options.data = objToStr(_options.data);
}
// 2.3 如果當前 data 有值, 且當前是 GET 方式, 那么可以提前 把 url 后拼接上 data
if (_options.data && /^(GET)$/i.test(_options.method)) {
_options.url += "?" + _options.data;
}
// 3. 封裝 ajax 請求
const p = new Promise((resolve, reject) => {
// 3.1 創建 ajax
const xhr = new XMLHttpRequest();
// 3.2 配置 ajax 請求信息
xhr.open(_options.method, _options.url, _options.async);
// 3.3 配置接收響應的事件
xhr.onload = function () {
if (_options.dataType === "string") {
resolve({
code: 1,
info: xhr.responseText,
msg: "成功",
});
return;
}
try {
const res = JSON.parse(xhr.responseText);
resolve({
code: 1,
info: res,
msg: "成功",
});
} catch (error) {
resolve({
code: 0,
info: error,
msg: "失敗",
});
}
};
// 如果當前的請求方式為 POST, 那么需要配置上對應的 請求頭
if (/^(POST)$/i.test(_options.method)) {
xhr.setRequestHeader(
"content-type",
_options.headers["content-type"]
);
}
// 如果 authorization 內有值, 需要帶上 authorization
if (_options.headers.authorization) {
xhr.setRequestHeader(
"authorization",
_options.headers.authorization
);
}
// 3.4 發送本次請求
/^(POST)$/i.test(_options.method)
? xhr.send(_options.data)
: xhr.send();
});
return p;
}
return ajax;
}
const res = outer("http://localhost:8888");
res({
url: "/test/first",
}).then(res => {
console.log(res)
});
res({
url: "/test/second",
dataType: 'json'
}).then(res => {
console.log(res)
});