转载自https://www.cnblogs.com/winyh/p/7053054.html
前端工程中发送 HTTP 请求从来都不是一件容易的事,前有骇人的 ActiveXObject
,后有 API 设计十分别扭的 XMLHttpRequest
,甚至这些原生 API 的用法至今仍是很多大公司前端校招的考点之一。
也正是如此,fetch 的出现在前端圈子里一石激起了千层浪,大家欢呼雀跃弹冠相庆恨不得马上把项目中的 $.ajax
全部干掉。然而,在新鲜感过后, fetch 真的有你想象的那么美好吗?
如果你还不了解 fetch,可以参考我的同事 @camsong 在 2015 年写的文章 《传统 Ajax 已死,Fetch 永生》
在开始「批斗」fetch之前,大家需要明确 fetch 的定位: fetch 是一个 low-level 的 API,它注定不会像你习惯的 $.ajax
或是 axios
等库帮你封装各种各样的功能或实现。 也正是因为这个定位,在学习或使用 fetch API 时,你会遇到不少的挫折。
(对于没有耐心看完全文的同学,请先记住本文的主旨不在于批评 fetch,事实上 fetch 的出现绝对是前端领域的进步体现。在了解主旨的前提下,关注 加黑 部分即可。)
发请求,比你想象的要复杂
很多人看到 fetch 的第一眼肯定会被它简洁的 API 吸引:
fetch('http://abc.com/tiger.png');
原来需要 new XMLHttpRequest 等小十行代码才能实现的功能如今一行代码就能搞定,能不让人动心吗!
但是当你真正在项目中使用时,少不了需要向服务端发送数据的过程,那么使用 fetch 发送一个对象到服务端需要几行代码呢?(出于兼容性考虑,大部分的项目在发送 POST 请求时都会使用 application/x-www-form-urlencoded 这种 Content-Type )
先来看看使用 jQuery 如何实现:
$.post('/api/add', {name: 'test'});
然后再看看 fetch 如何处理:
fetch('/api/add', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }, body: Object.keys({name: 'test'}).map((key) => { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); }).join('&')});
等等, body 字段那一长串代码在干什么? 因为 fetch 是一个 low-level 的 API,所以你需要自己 encode HTTP 请求的 payload,还要自己指定 HTTP Header 中的 Content-Type 字段。
这样就结束了吗?如果你在自己的项目中这样发送 POST 请求,很可能会得到一个 401 Unauthorized 的结果(视你的服务端如何处理无权限的情况而定)。如果你在仔细看一遍文档,会发现原来 fetch 在发送请求时默认不会带上 Cookie!
好,我们让 fetch 带上 Cookie:
fetch('/api/add', { method: 'POST', credentials: 'include', ...});
这样,一个最基础的 POST 请求才算能够发出去。
同理,如果你需要 POST 一个 JSON 到服务端,你需要这样做:
fetch('/api/add', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json;charset=UTF-8' }, body: JSON.stringify({name: 'test'})});
相比于 $.ajax 的封装,是不是复杂的不是一点半点呢?
错误处理,比你想象的复杂
按理说,fetch 是基于 Promise 的 API,每个 fetch 请求会返回一个 Promise 对象,而 Promise 的异常处理且不论是否方便,起码大家是比较熟悉的了。然而 fetch 的异常处理,还是有不少门道。
假如我们用 fetch 请求一个不存在的资源:
fetch('xx.png') .then(() => { console.log('ok');}).catch(() => { console.log('error');});
按照我们的惯例 console 应该要打印出 「error」才对,可事实又如何呢?有图有真相:
[图片上传失败...(image-1c4128-1512044596159)]
为什么会打印出 「ok」呢?
按照 MDN 的 说法 ,fetch 只有在遇到网络错误的时候才会 reject 这个 promise,比如用户断网或请求地址的域名无法解析等。只要服务器能够返回 HTTP 响应(甚至只是 CORS preflight 的 OPTIONS 响应),promise 一定是 resolved 的状态。
所以要怎么判断一个 fetch 请求是不是成功呢?你得用 response.ok
这个字段:
fetch('xx.png') .then((response) => { if (response.ok) { console.log('ok'); } else { console.log('error'); }}).catch(() => { console.log('error');});
再执行一次,终于看到了正确的日志:
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。