使用 Fetch完成AJAX請求
寫在前面
無論用JavaScript發送或獲取信息,我們都會用到Ajax。Ajax不需要刷新頁面就能發送和獲取信息,能使網頁實現異步更新。
幾年前,初始化Ajax一般使用jQuery的ajax方法:
$.ajax('some-url', {
success: (data) => { /* do something with the data */ },
error: (err) => { /* do something when an error happens */}
});
也可以不用jQuery,但不得不使用XMLHttpRequest,然而這是[相當復雜]
幸虧,瀏覽器現在支持Fetch API,可以無須其他庫就能實現Ajax
瀏覽器支持
fetch是相對較新的技術,當然就會存在瀏覽器兼容性的問題,當前各個瀏覽器低版本的情況下都是不被支持的,因此為了在所有主流瀏覽器中使用fetch 需要考慮 fetch 的 polyfill 了
require('es6-promise').polyfill();
require('isomorphic-fetch');
引入這兩個文件,就可以支持主流瀏覽器了
Fetch獲取數據解讀
使用Fetch獲取數據很容易。只需要Fetch你想獲取資源。
假設我們想通過GitHub獲取一個倉庫,我們可以像下面這樣使用:
fetch('https://api.github.com/users/chriscoyier/repos');
Fetch會返回Promise,所以在獲取資源后,可以使用.then方法做你想做的。
fetch('https://api.github.com/users/chriscoyier/repos')
.then(response => {/* do something */})
如果這是你第一次遇見Fetch,也許驚訝於Fetch返回的response。如果console.log返回的response,會得到下列信息:
{
body: ReadableStream
bodyUsed: false
headers: Headers
ok : true
redirected : false
status : 200
statusText : "OK"
type : "cors"
url : "http://some-website.com/some-url"
__proto__ : Response
}
可以看出Fetch返回的響應能告知請求的狀態。從上面例子看出請求是成功的(ok是true,status是200),但是我們想獲取的倉庫名卻不在這里。
顯然,我們從GitHub請求的資源都存儲在body中,作為一種可讀的流。所以需要調用一個恰當方法將可讀流轉換為我們可以使用的數據。
Github返回的響應是JSON格式的,所以調用response.json方法來轉換數據。
還有其他方法來處理不同類型的響應。如果請求一個XML格式文件,則調用response.text。如果請求圖片,使用response.blob方法。
所有這些方法(response.json等等)返回另一個Promise,所以可以調用.then方法處理我們轉換后的數據。
fetch('https://api.github.com/users/chriscoyier/repos')
.then(response => response.json())
.then(data => {
// data就是我們請求的repos
console.log(data)
});
可以看出Fetch獲取數據方法簡短並且簡單。
接下來,讓我們看看如何使用Fetch發送數據。
Fetch發送數據解讀
使用Fetch發送也很簡單,只需要配置三個參數。
fetch('some-url', options);
第一個參數是設置請求方法(如post、put或del),Fetch會自動設置方法為get。
第二個參數是設置頭部。因為一般使用JSON數據格式,所以設置ContentType為application/json。
第三個參數是設置包含JSON內容的主體。因為JSON內容是必須的,所以當設置主體時會調用JSON.stringify。
實踐中,post請求會像下面這樣:
let content = {some: 'content'};
// The actual fetch request
fetch('some-url', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(content)
})
// .then()...
API
fetch(url, { // url: 請求地址
method: "GET", // 請求的方法POST/GET等
headers: { // 請求頭(可以是Headers對象,也可是JSON對象)
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: , // 請求發送的數據 blob、BufferSource、FormData、URLSearchParams(get或head方法中不能包含body)
cache: 'default', // 是否緩存這個請求
credentials: 'same-origin', //要不要攜帶 cookie 默認不攜帶 omit、same-origin 或者 include
mode: "",
/*
mode,給請求定義一個模式確保請求有效
same-origin:只在請求同域中資源時成功,其他請求將被拒絕(同源策略)
cors : 允許請求同域及返回CORS響應頭的域中的資源,通常用作跨域請求來從第三方提供的API獲取數據
cors-with-forced-preflight:在發出實際請求前執行preflight檢查
no-cors : 目前不起作用(默認)
*/
}).then(resp => {
/*
Response 實現了 Body, 可以使用 Body 的 屬性和方法:
resp.type // 包含Response的類型 (例如, basic, cors).
resp.url // 包含Response的URL.
resp.status // 狀態碼
resp.ok // 表示 Response 的成功還是失敗
resp.headers // 包含此Response所關聯的 Headers 對象 可以使用
resp.clone() // 創建一個Response對象的克隆
resp.arrayBuffer() // 返回一個被解析為 ArrayBuffer 格式的promise對象
resp.blob() // 返回一個被解析為 Blob 格式的promise對象
resp.formData() // 返回一個被解析為 FormData 格式的promise對象
resp.json() // 返回一個被解析為 Json 格式的promise對象
resp.text() // 返回一個被解析為 Text 格式的promise對象
*/
if (resp.status === 200) return resp.json();
// 注: 這里的 resp.json() 返回值不是 js對象,通過 then 后才會得到 js 對象
throw New Error('false of json');
}).then(json => {
console.log(json);
}).catch(error => {
consolr.log(error);
})
常用情況
1.請求 json
fetch('http://xxx/xxx.json').then(res => {
return res.json();
}).then(res => {
console.log(res);
})
2. 請求文本
fetch('/xxx/page').then(res => {
return res.text();
}).then(res => {
console.log(res);
})
3. 發送普通json數據
fetch('/xxx', {
method: 'post',
body: JSON.stringify({
username: '',
password: ''
})
});
4. 發送Form表單數據
var form = document.querySelector('form');
fetch('/xxx', {
method: 'post',
body: new FormData(form)
});
5. 獲取圖片
fetch('/xxx').then(res => {
return res.blob();
}).then(res => {
document.querySelector('img').src = URL.createObjectURL(imageBlob);
})
6. 上傳文件
var file = document.querySelector('.file')
var data = new FormData()
data.append('file', file.files[0])
fetch('/xxx', {
method: 'POST',
body: data
})
7. 封裝
require('es6-promise').polyfill();
require('isomorphic-fetch');
export default function request(method, url, body) {
method = method.toUpperCase();
if (method === 'GET') {
body = undefined;
} else {
body = body && JSON.stringify(body);
}
return fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body
}).then((res) => {
if (res.status >= 200 && res.status < 300) {
return res;
} else {
return Promise.reject('請求失敗!');
}
})
}
export const get = path => request('GET', path);
export const post = (path, body) => request('POST', path, body);
export const put = (path, body) => request('PUT', path, body);
export const del = (path, body) => request('DELETE', path, body);
參考
總結
Fetch是很好的方法,能發送和接收數據。不需要在編寫XHR請求或依賴於jQuery。
盡管Fetch很好,但是其錯誤處理不是很直接。在處理之前,需要讓錯誤信息進入到
catch方法中。聽說有個zlFetch庫,改善了Fetch錯誤處理de問題。
But ! 現在有了Axios了,還跟我扯啥犢子呢!直接一步到位用Axios吧小盆友~
