首先声明一下,本文不是要讲解fetch的具体用法,不清楚的可以参考 教程。
fetch默认不携带cookie
配置其 credentials 项,其有3个值:
- omit: 默认值,忽略cookie的发送
- same-origin: 表示cookie只能同域发送,不能跨域发送
- include: cookie既可以同域发送,也可以跨域发送
credentials所表达的含义,其实与XHR2中的withCredentials属性类似,表示请求是否携带cookie
这样,若要fetch请求携带cookie信息,只需设置一下credentials选项即可,例如fetch(url, {credentials: 'include'});
fetch请求对某些错误http状态不会reject
这主要是由fetch返回promise导致的,因为fetch返回的promise在某些错误的http状态下如400、500等不会reject,相反它会被resolve;只有网络错误会导致请求不能完成时,fetch 才会被 reject;所以一般会对fetch请求做一层封装,例如下面代码所示:
function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } const error = new Error(response.statusText); error.response = response; throw error;}function parseJSON(response) { return response.json();}export default function request(url, options) { let opt = options||{}; return fetch(url, { credentials: 'include', ...opt}) .then(checkStatus) .then(parseJSON) .then((data) => ( data )) .catch((err) => ( err ));}复制代码
fetch不支持超时timeout处理
方法一:单纯setTimeout方式
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) } ) })}复制代码
当然在上面基础上可以模拟类似XHR的abort功能:
var oldFetchfn = fetch; window.fetch = function(input, opts){ return new Promise(function(resolve, reject){ var abort_promise = function(){ reject(new Error("fetch abort")) }; var p = oldFetchfn(input, opts).then(resolve, reject); p.abort = abort_promise; return p; })}复制代码
方法二:利用Promise.race方法 Promise.race方法接受一个promise实例数组参数,表示多个promise实例中任何一个最先改变状态,那么race方法返回的promise实例状态就跟着改变,具体可以参考这里。
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])}复制代码
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) })复制代码
fetch不支持progress事件
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是不支持有关progress事件的;不过可喜的是,根据fetch的指导规范标准,其内部设计实现了Request和Response类;其中Response封装一些方法和属性,通过Response实例可以访问这些方法和属性,例如response.json()、response.body等等;
值得关注的地方是,response.body是一个可读字节流对象,其实现了一个getRender()方法,其具体作用是:
getRender()方法用于读取响应的原始字节流,该字节流是可以循环读取的,直至body内容传输完成;
因此,利用到这点可以模拟出fetch的progress
// fetch() returns a promise that resolves once headers have been receivedfetch(url).then(response => { // response.body is a readable stream. // Calling getReader() gives us exclusive access to the stream's content var reader = response.body.getReader(); var bytesReceived = 0; // read() returns a promise that resolves when a value has been received reader.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 Uint8Array bytesReceived += result.value.length; console.log('Received', bytesReceived, 'bytes of data so far'); // Read some more, and call this function again return reader.read().then(processResult); });});复制代码
fetch跨域问题
fetch的mode配置项有3个值,如下:
-
same-origin:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;其对应的response type为basic。
-
cors: 该模式支持跨域请求,顾名思义它是以CORS的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS支持;其对应的response type为cors。
-
no-cors: 该模式用于跨域请求但是服务器不带CORS响应头,也就是服务端不支持CORS;这也是fetch的特殊跨域请求方式;其对应的response type为opaque。