XMLHttpRequest
使用上不便,就算是標準化後的 XMLHttpRequest Level 1 也只是功能上的加強,開發者通常會進一步地使用程式庫封裝,例如〈封裝 Ajax 操作〉做的那些事情。
2014 年 HTML5 正式標準化,Fetch API 是 HTML5 的一部份,Google、Mozilla 在 2015 年於瀏覽器開始提供實作。
從設計的角度來看,Fetch API 就像是集合了過去 Ajax 使用上一些好實踐的集合體,實現了職責分離,Fetch 的工廠函式 fetch
可接受選項物件,而傳回值是個 Promise
。
直接來看看〈建立 XMLHttpRequest 物件〉中取得表格的範例,若改用 Fetch 的話會如何簡化:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<button id='req'>取得表格</button>
<div id="table"></div>
<script type="text/javascript">
document.getElementById('req').onclick = function() {
fetch('XMLHttpRequest-1.txt')
.then(resp => resp.text())
.then(text => document.getElementById('table').innerHTML = text);
};
</script>
</body>
</html>
fetch
會傳回 Promise
,結果會是個 Response
實例,可以透過 text
方法取得承諾結果為回應文字的 Promise
;fetch
預設使用 GET
,若需要進一步設定請求資訊,可以使用一個初始物件作為第二個引數,例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
新增書籤:<br>
網址:<input id="url" type="text">
<span id="message" style="color:red"></span><br>
名稱:<input type="text">
<script type="text/javascript">
function params(paraObj) {
return Object.keys(paraObj)
.map(name => {
let paraName = encodeURIComponent(name);
let paraValue = encodeURIComponent(paraObj[name]);
return `${paraName}=${paraValue}`.replace(/%20/g, '+');
})
.join('&');
}
document.getElementById('url').onblur = function() {
let reqString = params({
url : document.getElementById('url').value
});
fetch('POST-1.php', {
method : 'POST',
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
},
body : reqString
})
.then(resp => resp.text())
.then(function(text) {
if(text === 'existed') {
document.getElementById('message').innerHTML = 'URL 已存在';
}
});
};
</script>
</body>
</html>
body
特性也支援 FormData
,例如將〈結合 FormData 上傳檔案〉中的範例,改使用 Fetch:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<form id="f" action="upload" method="post" enctype="multipart/form-data">
Photo :<input type="file" name="photo"/><br>
<input id="upload" type="submit"/>
</form>
<span id="message"></span>
<script type="text/javascript">
document.getElementById('upload').onclick = function(evt) {
let formData = new FormData(document.getElementById('f'));
fetch('upload', {
method : 'POST',
body : formData
})
.then(function(resp) {
if(resp.status === 200) {
document.getElementById('message').innerHTML = 'File Uploaded';
}
});
evt.preventDefault();
};
</script>
</body>
</html>
Fetch 也支援跨站請求,可透過初始物件的 mode
特性來設定 'cors'
(預設值)、'no-cors'
(會發出跨域請求,然而無法讀取回應)、'same-origin'
(不發出跨域請求) 等值,例如〈跨站請求 CORS〉中的範例,可以如下改用 Fetch:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<body>
ID:<input id="id">
<button id="test">JSONP 測試</button>
<span id="result"></span>
</body>
<script type="text/javascript">
document.getElementById('test').onclick = function() {
let id = document.getElementById('id').value;
let result = document.getElementById('result');
fetch(`https://openhome.cc/Gossip/ECMAScript/samples/CORS-1.php?id=${id}`, {
mode : 'cors' // 'cors' 是預設值
})
.then(resp => resp.json())
.then(person => result.innerHTML = `${person.name}, ${person.age}`);
};
</script>
</body>
</html>
其他初始可以設定的特性,可以參考〈WindowOrWorkerGlobalScope.fetch〉文件的說明。
可以看到的是,對於簡單的請求,使用 Fetch 的方便性,遠大於 XMLHttpRequest
。