Fetch API
回顧一下XMLHttpRequest的問題
- 所有的功能全部集中在一個對象上, 容易書寫出混亂而且不容易維護的代碼
- 採用傳統的事件驅動模式, 無法適配新的 Promise api
Fetch Api的特點
- 並非取代ajax, 而是對ajax傳統api的改進
- 精細的功能分割: 頭部信息, 請求信息, 響應信息等均分佈到不同的對象, 更利於處理各種複雜的ajax場景
- 使用Promise api, 更利於異步代碼的書寫
- fetch屬於web api, 理所當然只能在瀏覽器端運行
Fetch Api的基本使用
參數
該函數有兩個參數:
- 必填: 字符串, 對應的是請求地址
//請求數據的函數如下
const getDefaultData = () => {
//在論壇中我找到了一個請求網易雲音樂別人已經寫好的接口,可以用來測試
const url = 'https://api.imjad.cn/cloudmusic/?type=song&id=32785674';
fetch(url);
}
// 假設我們的html頁面中有一個按鈕, 點擊這個按鈕就會開始請求數據,然後我們現在獲取到這個按鈕
const btn = document.querySelector('button');
btn.onclick = () => {
getDefaultData();
}
當上面的代碼寫好, 我們進行點擊操作時, 服務器相應的給我們返回了數據
請求配置對象(紅色爲比較常用的, 綠色爲目前不太常用的)
- method: 字符串, 請求方法, 默認值GET
- headers: 對象, 請求頭信息
- body: 請求體的內容, 必須匹配請求頭中Content-Type
- mode:字符串, 請求模式
- cors: 默認值, 配置爲該值, 會在請求頭中加入origin和referer
- no-cors: 配置爲該值, 將不會再請求頭中加入origin和referer, 跨域的時候可能會出現問題
- same-origin: 配置爲該值, 則指示請求必須在同一個域中發生, 如果請求其他域, 則會報錯
- credentials: 如何攜帶憑據
- omit: 默認值, 不攜帶cookie
- same-origin: 請求同源地址時攜帶cookie
-include: 請求任何地址都要攜帶cookie
- omit: 默認值, 不攜帶cookie
- cache: 配置緩存模式
- default: 表示fetch請求之前將檢查一下http的緩存
- no-store: 表示fetch請求將完全忽略http緩存的存在, 這意味着請求之前將不再檢查http的緩存, 拿到響應以後他也不會再更新http緩存
- no-cache: 如果存在緩存, 那麼fetch將發送一個條件查詢request和一個正常的request, 拿到響應以後, 他會更新http緩存
- reload: 表示fetch請求之前將忽略http緩存的存在, 但是在請求得到響應以後, 他將主動更新http緩存
- force-cache: 表示fetch請求不顧一切的依賴緩存, 即使緩存過期了, 他依然從緩存中讀取, 除非沒有任何緩存, 那麼他纔會發送一個正常的request
- only-if-cached: 表示fetch請求不顧一切的依賴緩存, 即使緩存過期了, 他依然從緩存中讀取, 如果沒有任何緩存, 那麼他將拋出一個錯誤
//配置對象書寫
const getData = function() {
const url = 'xxx/api';
const config = {
method: 'POST', // 寫請求方法
headers: {
// 配置請求頭
//例如: "Content-Type": 'application/json'
},
body: {
// 配置請求體, 比如POST請求要傳遞的數據
}
//...其他不常用配置
}
fetch(url, config); // 開始請求數據
}
返回值
fetch函數返回一個Promise對象
- 當收到服務器的返回結果以後, Promise進入resolved狀態, 狀態數據爲Response對象(服務器不一定要200返回, 只要返回了東西都會走resolved狀態)
const getDefaultData = async () => {
const url = 'https://api.imjad.cn/cloudmusic/?type=song&id=32785674';
const config = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
body: {
},
// mode: 'cors'
}
// 既然返回的結果是一個Promise, 那麼我們就可以用await等待服務器響應的結果
try {
let promise = await fetch(url);
console.log(promise);
}catch(err) {
console.log(err);
}
}
const btn = document.querySelector('button');
//當按鈕點擊的時候我們開始數據請求
btn.onclick = function () {
getDefaultData();
}
- 當網絡發生錯誤(或者其他導致無法完成交互的錯誤), Promise進入rejected狀態, 狀態數據爲錯誤信息
Response對象(服務器響應對象)
- ok: boolean, 當響應消息在200-299之間時爲true, 其他爲false
- status: number 響應的狀態碼
- text(): 用於處理文本格式的ajax響應, 他從響應中獲取文本流, 將其讀完, 然後返回一個被解決爲string對象的Promise
const result = await fetch(url, config);
const respText = await result.text();
- blob(): 用於處理二進制的文件格式(比如圖片和電子表格)的ajax響應, 他讀取文件的原始數據, 一旦讀取完整個文件, 就返回一個被解決爲blob對象的Promise
const result = await fetch(url, config);
const respBlob = await result.blob();
- json(): 用於處理JSON格式的Ajax的響應, 他講json數據流轉化爲一個被解決爲JavaScript對象的Promise
const result = await fetch(url, config);
const respJson = await result.json();
- redirect(): 用於重定向到另一個url, 他會創建一個新的Promise, 以解決來自重定向的url響應
響應對象示例
來聊聊Request對象
除了使用基本的fetch方法, 還可以通過創建一個request對象來完成請求, 實際上fetch的內部也會幫你創建一個內部對象
//request對象的創建方式
new Request(url, config)
當我們遇到要複用某些請求的需求時, 就可以用到request對象
function getRespInfo() {
const url = 'xxx/api';
const config = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
}
//...body和其他配置
const request = new Request(url, config);
}
}
function async getData() {
// 每次都走一次getRespInfo這個函數, 保證每次創建的都是一個新的request對象
const result = await fetch(getRespInfo());
const respText = await result.text();
}
注意點: 儘量保證每次請求都是一個新的request對象
再來說說這個Response對象
大多數時候, Response對象都不需要我們自己來構建, 因爲服務器會幫我們封裝好, 但是在某些特殊時候, 比如後端數據還沒寫好, 在這種情況下,我們可能最先想到的就是mock數據, 但是我們也可以手動構建一個Response, 用來幫我們構建測試環境
// 我們有一個用來獲取省市的函數
async function getProvince() {
// 手動構建的Response對象
const resp = new Response(
`[
{"id": 1, "name": "beijing"},
{"id": 2, "name": "shanghai"}
]`,
ok: true,
status: 200
)
const result = await getJSON(resp);
console.log(result);
}
async function getJSON(resp) {
return await resp.json();
}
但是因爲我們大部分時間其實不會這樣進行測試, 所以僅瞭解即可
[擴展] Headers對象
在Request和Response對象內部, 會將傳遞配置中的headers請求頭對象轉化爲Headers對象
同時Headers對象提供一些方法:
- has(key): 判斷請求頭中是否存在指定的key
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const resp = new Request(url, config);
console.log(resp.headers.has('Content-Type')); //返回true, 因爲我們請求頭中配置了
- get(key): 得到請求頭中對應的key所對應的值
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const resp = new Request(url, config);
console.log(resp.headers.get('Content-Type')); //返回'application/json'
- set(key, value): 修改請求頭中對應的鍵值對, 如果是修改未存在的, 則會新建一個鍵值對
// 其餘代碼跟上面兩個方法一樣
resp.headers.set(c, 'helloworld'); //這時候請求頭中會多出一個c 並且值爲'helloworld'
- append(key, value): 專門用來往請求頭中添加鍵值對
//其餘代碼跟上面三個方法一樣
resp.headers.append(d, 'its tom'); //網請求頭中添加一個d, 值爲'its tom'
需要注意的是, 如果是對重複的屬性用append,並不會覆蓋之前的屬性而是合併
// 假設headers中有a 值爲1,然後我們調用append
resp.headers.append(a, 3); // 這時候headers中a的值爲1,2
- keys(): 得到所有的請求頭的key所組成的集合(iterator)
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json',
'a': 1
}
}
const resp = new Request(url, config);
console.log(resp.headers.keys());
- values(): 得到所有的請求頭key對應的值所組成的集合(iterator)
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json',
'a': 1
}
}
const resp = new Request(url, config);
console.log(resp.headers.values());
- entries(): 得到所有的請求頭鍵值對所組成的集合(iterator)
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json',
'a': 1
}
}
const resp = new Request(url, config);
console.log(resp.headers.entries());