fetch簡介: 新一代Ajax API
AJAX半遮半掩的底層API是飽受詬病的一件事情. XMLHttpRequest 並不是專為Ajax而設計的. 雖然各種框架對 XHR 的封裝已經足夠好用, 但我們可以做得更好。更好用的API是 fetch
。下面簡單介紹 window.fetch
方法, 在最新版的 Firefox 和 Chrome 中已經提供支援。
XMLHttpRequest
在我看來 XHR 有點複雜, 我不想解釋為什麼“XML”是大寫,而“Http”是“駱峰式”寫法。使用XHR的方式大致如下:
// 獲取 XHR 非常混亂! if (window.XMLHttpRequest) { // Mozilla, Safari, ... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } } // 開啟連線, 傳送資料. request.open('GET', 'https://davidwalsh.name/ajax-endpoint', true); request.send(null);
我們可以看出, XHR 其實是很雜亂的; 當然, 通過 JavaScript 框架可以很方便地使用XHR。
fetch
的基本使用
fetch
是全域性量 window
的一個方法, 第一個引數是URL:
// url (必須), options (可選)
fetch('/some/url', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// 出錯了;等價於 then 的第二個引數,但這樣更好用更直觀 :(
});
// 對響應的簡單處理 fetch('/some/url').then(function(response) { }).catch(function(err) { // 出錯了;等價於 then 的第二個引數,但這樣更直觀 :( }); // 鏈式處理,將非同步變為類似單執行緒的寫法: 高階用法. fetch('/some/url').then(function(response) { return //... 執行成功, 第1步... }).then(function(returnedValue) { // ... 執行成功, 第2步... }).catch(function(err) { // 中途任何地方出錯...在此處理 :( });
如果你還不習慣 then
方式的寫法,那最好學習一下,因為很快就會全面流行。
請求頭(Request Headers)
自定義請求頭資訊極大地增強了請求的靈活性。我們可以通過 new Headers()
來建立請求頭:
// 建立一個空的 Headers 物件,注意是Headers,不是Header var headers = new Headers(); // 新增(append)請求頭資訊 headers.append('Content-Type', 'text/plain'); headers.append('X-My-Custom-Header', 'CustomValue'); // 判斷(has), 獲取(get), 以及修改(set)請求頭的值 headers.has('Content-Type'); // true headers.get('Content-Type'); // "text/plain" headers.set('Content-Type', 'application/json'); // 刪除某條請求頭資訊(a header) headers.delete('X-My-Custom-Header'); // 建立物件時設定初始化資訊 var headers = new Headers({ 'Content-Type': 'text/plain', 'X-My-Custom-Header': 'CustomValue' });
可以使用的方法包括: append, has, get, set, 以及 delete 。需要建立一個 Request
物件來包裝請求頭:
var request = new Request('/some-url', {
headers: new Headers({
'Content-Type': 'text/plain'
})
});
fetch(request).then(function() { /* handle response */ });
下面介紹 Response
和Request
的使用方法!
Request 簡介
Request 物件表示一次 fetch 呼叫的請求資訊。傳入 Request 引數來呼叫 fetch, 可以執行很多自定義請求的高階用法:
method
- 支援GET
,POST
,PUT
,DELETE
,HEAD
url
- 請求的 URLheaders
- 對應的Headers
物件referrer
- 請求的 referrer 資訊mode
- 可以設定cors
,no-cors
,same-origin
credentials
- 設定 cookies 是否隨請求一起傳送。可以設定:omit
,same-origin
redirect
-follow
,error
,manual
integrity
- subresource 完整性值(integrity value)cache
- 設定 cache 模式 (default
,reload
,no-cache
)
Request
的示例如下:
var request = new Request('/users.json', {
method: 'POST',
mode: 'cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/plain'
})
});
// 使用!
fetch(request).then(function() { /* handle response */ });
只有第一個引數 URL 是必需的。在 Request
物件建立完成之後, 所有的屬性都變為只讀屬性. 請注意, Request
有一個很重要的 clone
方法, 特別是在 Service Worker API 中使用時 —— 一個 Request 就代表一串流(stream), 如果想要傳遞給另一個 fetch
方法,則需要進行克隆。
fetch
的方法簽名(signature,可理解為配置引數), 和 Request
很像, 示例如下:
fetch('/users.json', {
method: 'POST',
mode: 'cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/plain'
})
}).then(function() { /* handle response */ });
因為 Request 和 fetch 的簽名一致, 所以在 Service Workers 中, 你可能會更喜歡使用 Request 物件。關於 ServiceWorker 的相關部落格請等待後續更新!
Response 簡介
Response 代表響應, fetch 的 then
方法接收一個 Response
例項, 當然你也可以手動建立 Response
物件 —— 比如在 service workers 中可能會用到. Response 可以配置的引數包括:
type
- 型別,支援:basic
,cors
url
useFinalURL
- Boolean 值, 代表url
是否是最終 URLstatus
- 狀態碼 (例如:200
,404
, 等等)ok
- Boolean值,代表成功響應(status 值在 200-299 之間)statusText
- 狀態值(例如:OK
)headers
- 與響應相關聯的 Headers 物件.
// 在 service worker 測試中手動建立 response
// new Response(BODY, OPTIONS)
var response = new Response('.....', {
ok: false,
status: 404,
url: '/'
});
// fetch 的 `then` 會傳入一個 Response 物件
fetch('/')
.then(function(responseObj) {
console.log('status: ', responseObj.status);
});
Response
提供的方法如下:
clone()
- 建立一個新的 Response 克隆物件.error()
- 返回一個新的,與網路錯誤相關的 Response 物件.redirect()
- 重定向,使用新的 URL 建立新的 response 物件..arrayBuffer()
- Returns a promise that resolves with an ArrayBuffer.blob()
- 返回一個 promise, resolves 是一個 Blob.formData()
- 返回一個 promise, resolves 是一個 FormData 物件.json()
- 返回一個 promise, resolves 是一個 JSON 物件.text()
- 返回一個 promise, resolves 是一個 USVString (text).
處理 JSON響應
假設需要請求 JSON —— 回撥結果物件 response 中有一個json()
方法,用來將原始資料轉換成 JavaScript 物件:
fetch('https://davidwalsh.name/demo/arsenal.json').then(function(response) {
// 轉換為 JSON
return response.json();
}).then(function(j) {
// 現在, `j` 是一個 JavaScript object
console.log(j);
});
當然這很簡單 , 只是封裝了 JSON.parse(jsonString)
而已, 但 json
方法還是很方便的。
處理基本的Text / HTML響應
JSON 並不總是理想的請求/響應資料格式, 那麼我們看看如何處理 HTML或文字結果:
fetch('/next/page')
.then(function(response) {
return response.text();
}).then(function(text) {
// <!DOCTYPE ....
console.log(text);
});
如上面的程式碼所示, 可以在 Promise 鏈式的 then
方法中, 先返回 text()
結果 ,再獲取 text 。
處理Blob結果
如果你想通過 fetch 載入影象或者其他二進位制資料, 則會略有不同:
fetch('flowers.jpg')
.then(function(response) {
return response.blob();
})
.then(function(imageBlob) {
document.querySelector('img').src = URL.createObjectURL(imageBlob);
});
響應 Body mixin 的 blob()
方法處理響應流(Response stream), 並且將其讀完。
提交表單資料(Posting Form Data)
另一種常用的 AJAX 呼叫是提交表單資料 —— 示例程式碼如下:
fetch('/submit', {
method: 'post',
body: new FormData(document.getElementById('comment-form'))
});
提交 JSON 的示例如下:
fetch('/submit-json', {
method: 'post',
body: JSON.stringify({
email: document.getElementById('email').value
answer: document.getElementById('answer').value
})
});
非常非常簡單, 媽媽再也不用擔心我的Ajax!
未完的故事(Unwritten Story)
fetch
是個很實用的API , 當前還不允許取消請求, 這使得很多程式設計師暫時不會考慮它。
新的 fetch
API 比起 XHR 更簡單也更智慧。畢竟,它就是專為AJAX而設計的, 具有後發優勢. 而我已經迫不及待地使用了, 即使現在相容性還不是那麼好!
翻譯時間: 2016年5月22日
原文時間: 2016年4月15日