目录
一、fetch 概述
二、fetch 的语法
1、实现一个简单的 fetch 请求
2、fetch 方法介绍
(1)、fetch 方法的第一个参数
(2)、fetch 方法的第二个参数
(3)、fetch 方法的返回值
3、检测 fetch() 请求是否成功
三、fetch 的应用
1、上传 JSON 数据
2、上传文件
3、上传多个文件
四、fetch 的实现
五、fetch 的问题的解决
1、解决 fetch 的兼容问题
2、解决 fetch 不支持 timeout 处理的问题
(1)、通过手动控制 promise 状态的实例来实现 fetch 的 timeout 功能
(2)、利用 Promise.race 方法代替实现 fetch 的 timeout 的功能
3、解决 fetch 不支持进度事件(Progress Event)
(1)、利用 response.body 模拟实现 fetch 的 progress 事件
(2)、使用 Promise+XHR 结合的方式实现类 fetch 的 progress 效果
4、解决 fetch 不支持 JSONP 跨域的问题
5、解决 fetch 的跨域问题
六、深入学习 fetch 的资源
fetch 是一种 HTTP 数据请求的方式,它不是 ajax 的进一步封装,而是 XMLHttpRequest(以下简称 XHR)的一种替代方案。
fetch 与 ajax 的区别:
fetch('http://example.com/movies.json').then(function(response) {return response.json();}).then(function(myJson) {console.log(myJson);});
fetch 方法接受两个参数:一个 URL 地址或一个 request 对象 和 (可选的)一个配置项对象。
除了传给 fetch() 一个 URL 地址,还可以通过使用 Request() 构造函数来创建一个 request 对象,然后再作为参数传给 fetch() 方法。
var myHeaders = new Headers();var myInit = { method: 'GET',headers: myHeaders,mode: 'cors',cache: 'default' };var myRequest = new Request('flowers.jpg', myInit);fetch(myRequest).then(function(response) {return response.blob();
}).then(function(myBlob) {var objectURL = URL.createObjectURL(myBlob);myImage.src = objectURL;
});
(可选的)一个配置项对象。该配置项包括所有对请求的设置。
配置项可选的参数有:
举个栗子:
// POST方法实现示例
postData('http://example.com/answer', {answer: 42}).then(data => console.log(data)) .catch(error => console.error(error))function postData(url, data) {// 默认值标记为 *return fetch(url, {body: JSON.stringify(data), // 必须与'Content-Type'标头匹配cache: 'no-cache', // 可选的值有*default, no-cache, reload, force-cache, only-if-cached。credentials: 'same-origin', // 可选的值有include, same-origin, *omitheaders: {'user-agent': 'Mozilla/4.0 MDN Example','content-type': 'application/json'},method: 'POST', // 可选的值有*GET, POST, PUT, DELETE等。mode: 'cors', // 可选的值有no-cors, cors, *same-originredirect: 'follow', // 可选的值有manual, *follow, errorreferrer: 'no-referrer', // 可选的值有*client, no-referrer}).then(response => response.json()) // 解析JSON响应
}
fetch 方法,总是返回一个包含响应结果的 Promise 对象。当该 Promise 对象为 resolve 状态时,在其回调函数中可获取 Response 对象。
Response 的可配置参数包括:
Response 提供的方法如下:
当 fetch 方法返回的 promise 对象的状态是 resolved 时,调用 then 方法,在 then 方法的回调函数中判断 response.ok 为 true 时,才表示 fetch() 请求是成功的。否则,fetch() 请求就是失败的。
fetch('flowers.jpg').then(function(response) {if(response.ok) {return response.blob();}throw new Error('Network response was not ok.');
}).then(function(myBlob) { var objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL;
}).catch(function(error) {console.log('There has been a problem with your fetch operation: ', error.message);
});
var url = 'https://example.com/profile';
var data = {username: 'example'};fetch(url, {method: 'POST', // 或者 'PUT'body: JSON.stringify(data), // 数据可以是“string”或{object}!headers: new Headers({'Content-Type': 'application/json'})
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
可以通过 HTML 元素,FormData() 和 fetch() 上传文件。
var formData = new FormData();
var fileField = document.querySelector("input[type='file']");formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);fetch('https://example.com/profile/avatar', {method: 'PUT',body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
可以通过HTML 元素,FormData() 和 fetch() 上传文件。
var formData = new FormData();
var photos = document.querySelector("input[type='file'][multiple]");formData.append('title', 'My Vegas Vacation');
// formData 只接受文件、Blob 或字符串,不能直接传递数组,所以必须循环嵌入
for (let i = 0; i
})
.then(response => response.json())
.then(response => console.log('Success:', JSON.stringify(response)))
.catch(error => console.error('Error:', error));
export default async(url = '', data = {}, type = 'GET', method = 'fetch') => {type = type.toUpperCase();url = baseUrl + url;if (type == 'GET') {let dataStr = ''; //数据拼接字符串Object.keys(data).forEach(key => {dataStr += key + '=' + data[key] + '&';})if (dataStr !== '') {dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));url = url + '?' + dataStr;}}if (window.fetch && method == 'fetch') {let requestConfig = {credentials: 'include',//为了在当前域名内自动发送 COOKIE , 必须提供这个选项method: type,headers: {'Accept': 'application/json','Content-Type': 'application/json'},mode: "cors",//请求的模式cache: "force-cache"}if (type == 'POST') {Object.defineProperty(requestConfig, 'body', {value: JSON.stringify(data)})}try {const response = await fetch(url, requestConfig);const responseJson = await response.json();return responseJson} catch (error) {throw new Error(error)}} else {return new Promise((resolve, reject) => {let requestObj;if (window.XMLHttpRequest) {requestObj = new XMLHttpRequest();} else {requestObj = new ActiveXObject;}let sendData = '';if (type == 'POST') {sendData = JSON.stringify(data);}requestObj.open(type, url, true);requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");requestObj.send(sendData);requestObj.onreadystatechange = () => {if (requestObj.readyState == 4) {if (requestObj.status == 200) {let obj = requestObj.responseif (typeof obj !== 'object') {obj = JSON.parse(obj);}resolve(obj)} else {reject(requestObj)}}}})}
}
支持 fetch 的浏览器版本有:Chrome、Firefox、Safari 6.1+ 和 IE 10+。
虽然,不是所有的浏览器都支持 fetch 请求,但是我们可以用window.fetch polyfill来处理兼容问题。
fetch 的 timeout 的特点:
实现 fetch 的 timeout 功能,其思想就是新创建一个可以手动控制promise状态的实例,根据不同情况来对新promise实例进行resolve或者reject,从而达到实现timeout的功能。
var oldFetchfn = fetch; //拦截原始的fetch方法
window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法return new Promise(function(resolve, reject){var timeoutId = setTimeout(function(){reject(new Error("fetch timeout"))}, opts.timeout);oldFetchfn(input, opts).then(res=>{clearTimeout(timeoutId);resolve(res)},err=>{clearTimeout(timeoutId);reject(err)})})
}
var oldFetchfn = fetch; //拦截原始的fetch方法
window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法var fetchPromise = oldFetchfn(input, opts);var timeoutPromise = new Promise(function(resolve, reject){setTimeout(()=>{reject(new Error("fetch timeout"))}, opts.timeout)});retrun Promise.race([fetchPromise, timeoutPromise])
}
Progress Events定义了与客户端服务器通信有关的事件。这些事件最早其实只针对XHR操作,但目前也被其它API借鉴。有以下6个进度事件:
Ajax 的 XHR 是原生支持 progress 事件的,比如:
var xhr = new XMLHttpRequest()
xhr.open('POST', '/uploads')
xhr.onload = function() {}
xhr.onerror = function() {}
function updateProgress (event) {if (event.lengthComputable) {var percent = Math.round((event.loaded / event.total) * 100)console.log(percent)}
xhr.upload.onprogress =updateProgress; //上传的progress事件
xhr.onprogress = updateProgress; //下载的progress事件
}
xhr.send();
但 fetch 就不支持该事件。不过,fetch 内部设计实现了 Request 和 Response 类。其中 Response 封装一些方法和属性,通过 Response 实例可以访问这些方法和属性,例如 response.json()、response.body 等等。
response.body是一个可读字节流对象,其实现了一个getRender()方法,其具体作用是:用于读取响应的原始字节流,该字节流是可以循环读取的,直至body内容传输完成。因此,利用到这点可以模拟出 fetch 的 progress。
// fetch() returns a promise that resolves once headers have been received
fetch(url).then(response => {// response.body is a readable stream.// Calling getReader() gives us exclusive access to the stream's contentvar reader = response.body.getReader();var bytesReceived = 0;// read() returns a promise that resolves when a value has been receivedreader.read().then(function processResult(result) {// Result objects contain two properties:// done - true if the stream has already given you all its data.// value - some data. Always undefined when done is true.if (result.done) {console.log("Fetch complete");return;}// result.value for fetch streams is a Uint8ArraybytesReceived += result.value.length;console.log('Received', bytesReceived, 'bytes of data so far');// Read some more, and call this function againreturn reader.read().then(processResult);});
});
function fetchProgress(url, opts={}, onProgress){return new Promise(funciton(resolve, reject){var xhr = new XMLHttpRequest();xhr.open(opts.method || 'get', url);for(var key in opts.headers || {}){xhr.setRequestHeader(key, opts.headers[key]);}xhr.onload = e => resolve(e.target.responseText)xhr.onerror = reject;if (xhr.upload && onProgress){xhr.upload.onprogress = onProgress; //上传}if ('onprogerss' in xhr && onProgress){xhr.onprogress = onProgress; //下载}xhr.send(opts.body)})
}
fetchProgress('/upload').then(console.log)
JSONP 是外链一个Javascript资源。
如何基于Promise来实现一个JSONP,并且使其看起来就像 fetch 支持 JSONP 一样?
可以使用 fetch-jsonp 插件。使用演示如下:
首先需要用 npm 安装 fetch-jsonp:
npm install fetch-jsonp --save-dev
然后在像下面一样使用:
fetchJsonp('/users.jsonp', {timeout: 3000,jsonpCallback: 'custom_callback'}).then(function(response) {return response.json()}).catch(function(ex) {console.log('parsing failed', ex)})
XHR 2 级 支持一种跨域:
fetch 支持两种跨域请求:
总的来说,fetch 的跨域请求是使用 CORS 方式,需要浏览器和服务端的支持。
Js中fetch方法:https://www.cnblogs.com/WindrunnerMax/p/13024711.html
传统 Ajax 已死,Fetch 永生:https://segmentfault.com/a/1190000003810652
XHR or Fetch API ?:http://jartto.wang/2017/01/17/xhr-or-fetch-api/
res.json() 与 res.send() 的区别?:https://andyli.blog.csdn.net/article/details/79753342