React 中使用 Ajax

React是一个视图层框架,用于构建用户界面。在 MVC 架构中,它仅仅负责视图部分。在实际的开发过程中,往往需要我们引入一些第三方组件,比如今天要说的AJAX API
Asynchronous Javascript And XML (异步的JavaScript和XML)
它并不是一种单一的技术,而是有机利用一系列交互式网页应用相关的技术所形成的结合体AJAX 是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。通过不同的方式(如 AxiosXMLHttpRequestfetch API )使用来进行 AJAX请求(GET、POST、PUT和DELETE),以获取、创建、更新和删除数据。


1、对JavaScriptPromise 比较熟悉,可以使用 Axios
2、可以使用 jQuery ,但不建议仅仅为了进行API调用而引入整个库。
3、喜欢使用前沿标准,可以使用 fetch API


axios是对原生XHR的封装。它有以下几大特性:

  • 可以在node.js中使用
  • 提供了并发请求的接口
  • 支持Promise API

它也是对原生XHR的封装,还支持JSONP,非常方便;真的是用过的都说好。但是随着react,vue等前端框架的兴起,jquery早已不复当年之勇。很多情况下我们只需要使用ajax,但是却需要引入整个jquery,这非常的不合理,于是便有了fetch的解决方案。

fetch号称是ajax的替代品,它的API是基于Promise设计的,旧版本的浏览器不支持Promise,需要使用polyfill es6-promise。Fetch API 很简单,看文档很快就学会了。推荐 MDN Fetch 教程 和 万能的WHATWG Fetch 规范。使用fetch之前,大家需要明确fetch的定位fetch 是一个 low-level 的 API,它注定不会像你习惯的 $.ajax 或是axios等库帮你封装各种各样的功能或实现。


1. 创建一个请求的实例

使用 XHR 发送一个 json 请求一般是这样:

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';

xhr.onload = function() {
  console.log(xhr.response);
};

xhr.onerror = function() {
  console.log("Oops, error");
};

xhr.send();

使用 ES6 的 箭头函数 后:

fetch(url,option).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))

现在看起来好很多了,但这种 Promise 的写法还是有 Callback 的影子,而且 promise 使用 catch 方法来进行错误处理的方式有点奇怪。不用急,下面使用 async/await 来做最终优化:

注:async/await 是非常新的 API,属于 ES7,目前尚在 Stage 1(提议) 阶段,这是它的完整规范。使用 Babel 开启 runtime 模式后可以把 async/await 无痛编译成 ES5 代码。也可以直接使用 regenerator 来编译到 ES5

try {
  let response = await fetch(url);
  let data = await response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}
// 注:这段代码如果想运行,外面需要包一个 async function

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

Promise,generator/yield,await/async 都是现在和未来 JS 解决异步的标准做法,可以完美搭配使用。这也是使用标准 Promise 一大好处。最近也把项目中使用第三方 Promise 库的代码全部转成标准 Promise,为以后全面使用 async/await 做准备。


2. 两者参数对比

  • request对象
options = {
  method:
  headers:
  body:
  credentials:
  mode:
  cache:
  redirect:
  referrer:
  referrerPolicy:
  integrity:
} 
接口参数 fetch XMLHttpRequest 默认
method,headers 请求方法,请求头 同样的字段可以获取
body 可以接收 Blob,ArrayBuffer,FormData,String 等多种形式的参数。 也可以接收这些类型的参数,但是如果在低版本的浏览器中像 Blob 就没有这种格式。所以传统的 Ajax 在这方面支持情况还取决于浏览器。
credentials omit: 请求不带任何 cookie。same-origin: 同域请求会带上 cookie。include: 无论是否跨域都会带上 cookie。 true: 大致对应上面的 include,MDN 上描述此 flag 不止控制 cookie 还控制 authorization headers 或者 TLS client certificates。false: 大致对应上面的 omit,MDN 上描述为 false 时还控制跨域请求的 response 不能设置 cookie。
mode 可以通过设置这个标志位从发起请求阶段就阻止跨域请求。 没有对应的标志位,只能先发出请求然后通过检测 response 头中是否有允许跨域的字段来判断是否要阻止接收 response。
cache 控制 fetch 请求如何和 HTTP 缓存协作的 没有对应的字段
redirect 设置请求如果遇到重定向的返回如何响应。follow=> 跟随重定向;error=>如果 response 是重定向,则报错;manual=> 自定义行为 没有对应的字段
referrer 设置请求 referrer 字段的值 没有对应的字段
referrerPolicy Referer 头如何被设置的策略 没有对应的字段
integrity 设置请求的 subresource integrity 没有对应的字段
  • response对象
fetch(url).then(function(response) {
  return response.json();
}).catch(function(e) {
  console.log("Oops, error");
});
接口参数 fetch XMLHttpRequest 默认
response.headers 获取响应的 HTTP 头 有一个 getAllResponseHeaders 方法可以获取,但是返回的是以 CRLF 分割的字符串,需要自己 parse 成对象格式
response.ok 表示请求成功,即 statsu 在 200-299 之间 没有对应的字段
response.status , response.statusText 状态码及状态文本信息 同样的字段可以获取
response.redirected 此标志位表示请求是否被重定向了 没有对应的字段
response.type 表示 response 的类型。basic=>常规同域响应。cors=>合法的跨域响应。error=> 网络错误。opaque=> 用"no-cors"请求去获取跨域资源的响应。 没有对应的字段
response.url 响应对应的 url,并且是最终的 url(在重定向后最终的url) responseURL 可以对应这个值,但是这个字段是新功能。所以传统的 Ajax 在这方面支持情况还取决于浏览器
response.arrayBuffer(), response.blob() , response.formData() 对应类型的数据的Promise对象 Ajax 是否能实现获取这些格式的方法,取决于浏览器是否实现了相关类
response.json(), response.text() 对应类型的数据的Promise对象 可以通过 XMLHttpRequest.response 或者 XMLHttpRequest.responseText 获取并 pasrse

3. 总结一下,Fetch 优点主要有:

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

Fetch 和标准 Promise 的不足

由于 Fetch 是典型的异步场景,所以大部分遇到的问题不是 Fetch 的,其实是 Promise 的。ES6 的 Promise 是基于 Promises/A+ 标准,为了保持 简单简洁 ,只提供极简的几个 API。如果你用过一些牛 X 的异步库,如 jQuery(不要笑) 、Q.js 或者 RSVP.js,可能会感觉 Promise 功能太少了。

没有 Deferred

Deferred 可以在创建 Promise 时可以减少一层嵌套,还有就是跨方法使用时很方便。
ECMAScript 11 年就有过 Deferred 提案,但后来没被接受。其实用 Promise 不到十行代码就能实现 Deferred:es6-deferred。现在有了 async/await,generator/yield 后,deferred 就没有使用价值了。

没有获取状态方法:isRejected,isResolved

标准 Promise 没有提供获取当前状态 rejected 或者 resolved 的方法。只允许外部传入成功或失败后的回调。我认为这其实是优点,这是一种声明式的接口,更简单。

缺少其它一些方法:always,progress,finally

always 可以通过在 then 和 catch 里重复调用方法实现。finally 也类似。progress 这种进度通知的功能还没有用过,暂不知道如何替代。

不能中断,没有 abort、terminate、onTimeout 或 cancel 方法

Fetch 和 Promise 一样,一旦发起,不能中断,也不会超时,只能等待被 resolve 或 reject。幸运的是,whatwg 目前正在尝试解决这个问题 whatwg/fetch#27

参考来源:
传统 Ajax 已死,Fetch 永生

2018-11-28

推荐阅读更多精彩内容