说明

由于 Fetch API 是基于 Promise 设计,有必要先学习一下 Promise。旧浏览器不支持 Promise,需要使用 polyfill es6-promise

如果不支持fetch也没有问题,可以使用第三方的ployfill来实现只会fetch:whatwg-fetch 这个用的人多。

开始

发送一个json请求

XHR方式

  1. var xhr = new XMLHttpRequest();
  2. xhr.open('GET', url);
  3. xhr.responseType = 'json';
  4. xhr.onload = function() {
  5. console.log(xhr.response);
  6. };
  7. xhr.onerror = function() {
  8. console.log("Oops, error");
  9. };
  10. xhr.send();

fetch方式

  1. fetch(url).then(function(res) {
  2. return res.json()
  3. }).then(function(data) {
  4. console.log(data);
  5. }).catch(function(e) {
  6. console.error(e)
  7. })

或者:

  1. fetch(url).then(response => response.json())
  2. .then(data => console.log(data))
  3. .catch(e => console.error(e))

再或者:

async/await 是非常新的 API,属于 ES7

  1. try{
  2. let res = await fetch(url)
  3. let data = await res.json()
  4. console.log(data)
  5. }catch(e) {
  6. console.error(e)
  7. }

使用 await 后,写异步代码就像写同步代码一样爽。await 后面可以跟 Promise 对象,表示等待 Promise resolve() 才会继续向下执行,如果 Promise 被 reject() 或抛出异常则会被外面的 try…catch 捕获。

fetch GET请求初步

  1. fetch(url, {
  2. method: 'GET'
  3. })
  4. .then((res) => {
  5. return res.text()
  6. })
  7. .then((res) => {
  8. console.log(res)
  9. })

fetch GET请求的参数传递

  1. fetch(url + '?a=1&b=2', { // 在URL中写上传递的参数
  2. method: 'GET'
  3. })
  4. .then((res) => {
  5. return res.text()
  6. })
  7. .then((res) => {
  8. console.log(res)
  9. })

fetch POST请求初步

  1. fetch(url, {
  2. method: 'POST' // 指定是POST请求
  3. })
  4. .then((res)=>{
  5. return res.text()
  6. })
  7. .then((res)=>{
  8. console.log(res)
  9. })

fetch POST请求参数的传递

  1. let formData = new FormData()
  2. formData.append('foo', 1)
  3. formData.append('bar', 2)
  4. fetch(url, {
  5. method: 'POST',
  6. body: formData,// 这里是请求对象
  7. })
  8. .then((res) => {
  9. return res.text()
  10. })
  11. .then((res) => {
  12. console.log(res)
  13. })

fetch 设置请求的头信息

在POST提交的过程中,一般是表单提交,可是,经过查询,发现默认的提交方式是:Content-Type:text/plain;charset=UTF-8,这个显然是不合理的。

  1. fetch(url, {
  2. method: 'POST',
  3. headers: new Headers({
  4. 'Content-Type': 'application/x-www-form-urlencoded' // 指定提交方式为表单提交
  5. }),
  6. body: new URLSearchParams([["foo", 1],["bar", 2]]).toString()
  7. })
  8. .then((res)=>{
  9. return res.text()
  10. })
  11. .then((res)=>{
  12. console.log(res)
  13. })

fetch 通过接口得到JSON数据

上面所有的例子中都是返回一个文本

  1. fetch(
  2. url, { // 在URL中写上传递的参数
  3. method: 'GET',
  4. headers: new Headers({
  5. 'Accept': 'application/json' // 通过头指定,获取的数据类型是JSON
  6. })
  7. })
  8. .then((res) => {
  9. return res.json() // 返回一个Promise,可以解析成JSON
  10. })
  11. .then((res) => {
  12. console.log(res) // 获取JSON数据
  13. })

fetch 强制带Cookie

默认情况下, fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于维护一个用户会话,则导致未经认证的请求(要发送 cookies,必须发送凭据头).
fetch(url, {
method: ‘GET’,
credentials: ‘include’ // 强制加入凭据头
})

fetch 简单封装一下

  1. /**
  2. * 将对象转成 a=1&b=2的形式
  3. * @param obj 对象
  4. */
  5. function obj2String(obj, arr = [], idx = 0) {
  6. for (let item in obj) {
  7. arr[idx++] = [item, obj[item]]
  8. }
  9. return new URLSearchParams(arr).toString()
  10. }
  11. /**
  12. * 真正的请求
  13. * @param url 请求地址
  14. * @param options 请求参数
  15. * @param method 请求方式
  16. */
  17. function commonFetcdh(url, options, method = 'GET') {
  18. const searchStr = obj2String(options)
  19. let initObj = {}
  20. if (method === 'GET') { // 如果是GET请求,拼接url
  21. url += '?' + searchStr
  22. initObj = {
  23. method: method,
  24. credentials: 'include'
  25. }
  26. } else {
  27. initObj = {
  28. method: method,
  29. credentials: 'include',
  30. headers: new Headers({
  31. 'Accept': 'application/json',
  32. 'Content-Type': 'application/x-www-form-urlencoded'
  33. }),
  34. body: searchStr
  35. }
  36. }
  37. fetch(url, initObj).then((res) => {
  38. return res.json()
  39. }).then((res) => {
  40. return res
  41. })
  42. }
  43. /**
  44. * GET请求
  45. * @param url 请求地址
  46. * @param options 请求参数
  47. */
  48. function GET(url, options) {
  49. return commonFetcdh(url, options, 'GET')
  50. }
  51. /**
  52. * POST请求
  53. * @param url 请求地址
  54. * @param options 请求参数
  55. */
  56. function POST(url, options) {
  57. return commonFetcdh(url, options, 'POST')
  58. }
  1. GET('https://www.baidu.com/search/error.html', {a:1,b:2})
  2. POST('https://www.baidu.com/search/error.html', {a:1,b:2})

支持

Fetch总结 - 图1
原生支持率并不高,幸运的是,引入下面这些 polyfill 后可以完美支持 IE8+ :

常见问题

  • Fetch 请求默认是不带 cookie 的,需要设置 fetch(url, {credentials: 'include'})
  • 服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
  • 所有版本的 IE 均不支持原生 Fetch,fetch-ie8 会自动使用 XHR 做 polyfill。但在跨域时有个问题需要处理。IE8, 9 的 XHR 不支持 CORS 跨域。
  • Response返回一个被解析后的promise对象和数据,有这些解析方式:arrayBuffer()、blob()、、json()、text()、formData()

参考

  1. 传统 Ajax 已死,Fetch 永生
  2. fetch,终于认识你

总结

  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 同构方便,使用isomorphic-fetch

注:同构(isomorphic/universal)就是使前后端运行同一套代码的意思,后端一般是指 NodeJS 环境。