详解前端异步请求 Ajax 和 Fetch

553 阅读10分钟

1. 简介

  • Ajax全称Asynchronous Javascript And XML(即异步的javascript和XML),它是现代大多数web应用程序做为数据交互的核心技术,它支持在页面不刷新的前提下,向web服务器异步的发送数据请求和接收,从而实现页面无刷新显示新的数据,提升交互体验。Ajax不是一种新的编程语言或者某一种技术,而是一个概念,是一种应用客户端脚本加载服务器数据的方法。
  • Fetch是一个现代的概念, 等同于XMLHttpRequest,它提供了许多与XMLHttpRequest相同的功能。被称为下一代Ajax技术,它采用Promise方式来处理数据,是一种简洁明了的API,比XMLHttpRequest更加简单易用。

2. 详解Ajax

ajax.png 实现Ajax的核心方法是使用XMLHttpRequest(XHR)对象,XHR对象用于服务器交互,通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。 XMLHttpRequest技术在1998年前后得到了应用,并在1999年首页作为非标准的Inter Explorer5.0 ActiveX组件出现,是微软开发用来支持基于浏览器的Outlook版本,XML是当时最流行的数据格式。 Ajax是2005年Jesse James Garrett在他的文章《AJAX:web应用程序方法》中提出的新术语,用来描述一种使用现有技术集合的方法,包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 以及最重要的XMLHttpRequest。

  • ajax示例
  // 创建XMLHttpRequest对象xhr
  var xhr = new XMLHttpRequest()
  // 设置xhr请求超时时间
  xhr.timeout = 3000
  // 设置响应数据格式
  xhr.reponseType = 'JSON'
  // 创建一个GET请求
  xhr.open('GET','/server',true)
  xhr.onload = function(e){
      if(this.status === 200 || this.status === 304){
          // 数据处理逻辑
          console.log(xhr.responseText)
      }
  }
  // xhr相关事件注册及回调
  xhr.ontimeout = functioin(e){...}
  xhr.onerror = functioin(e){...}
  xhr.upload.onprogress = functioin(e){...}
  // 发送数据
  xhr.send()

XMLHttpRequest API

属性

  • onreadystatechange:当readyState属性发生变化时调用的EventHandler
  • readyState(只读):对象有以下几种状态 值|状态|描述 --|:--:|-- 0 |UNSENT |对象已经建立,但还没有调用open方法 1 |OPENED |已经调用open的方法,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错 2 |HEADERS_RECEIVED |已经调用open方法,头部信息已经是可用的 3 |LOADING |正在加载,服务器已经返回一些数据 4 |DONE |整个发送和接收数据完成
  • response(只读):返回ArrayBuffer、Blob、Document、DOMString,具体是哪种类型取决于XMLHttpRequest.responseType的值。其中包含响应体body
  • responseText(只读):返回一个DOMString,该DOMString包含对请求的响应,如果请求未成功或尚未发送,则返回null
  • responseType:设置服务器返回的数据类型 值|状态|描述 --|:--:|-- "" |DOMString |对象已经建立,但还没有调用open方法 arraybuffer |ArrayBuffer |已经调用open的方法 blob |Blob |已经调用open方法,头部信息已经是可用的 document |Document |正在加载,服务器已经返回一些数据 json |JSON |整个发送和接收数据完成 text |DOMString |整个发送和接收数据完成
  • responseXML:仅当responseType为""、"text"或"document"时可用,类似response,返回响应的Document类型的正文
  • responseURL:返回响应的序列化URL,如果URL为空则返回空串。URL不包含锚点#后面的内容。如果有重定向,返回最终的URL
  • status:返回响应状态码,请求完成前或请求发生错误,返回0
  • statusText:返回响应状态,与status不同的是,它包括响应状态的整个文本(例如,“200 OK”)
  • timeout:设置超时时间,以毫秒为单位。默认0,不超时
  • ontimeout:继承自XMLHttpRequestEventTarget,请求超时时调用
  • upload:返回XMLHttpRequestUpload对象。可以绑定事件追踪上传进度
  • XMLHttpRequest事件 事件|事件类型 --|-- onloadstart |获取开始 onprogress |数据传输进行中 onabort |获取操作终止 onerror |获取失败 onload |获取成功 ontimeout |超时 onloadend |获取完成(成功或失败)
  • withCredentials:用来指定跨域的请求是否应该使用证书(如cookie或授权header头)
  • channel:对象在执行请求时使用的通道
  • mozAnon:请求将在没有cookie和身份验证header头的情况下发送
  • mozSystem:请求时不会强制执行同源策略
  • mozBackgroundRequest:指示对象是否是后台服务器端的请求

方法

  • open:初始化一个请求
  • abort():终止请求,readyState属性将被置为0
  • getAllResponseHeaders():获取响应头,返回以\r\n分隔的字符串
  • getResponseHeader():获取指定的响应头 参数|描述 --|-- method |要使用的HTTP方法,比如「GET」、「POST」、「PUT」、「DELETE」、等。对于非HTTP(S) URL被忽略 url |一个DOMString表示要向其发送请求的URL async可选 |一个可选的布尔参数,默认为true,表示要不要异步执行操作。如果值为false,send()方法直到收到答复前不会返回。如果true,已完成事务的通知可供事件监听器使用。如果multipart属性为true则这个必须为true,否则将引发异常 user可选 |可选 可选的用户名用于认证用途;默认为null password可选 |可选 可选的密码用于认证用途,默认为null
  • overrideMimeType:重写由服务器返回的MIME type
  • send:发送请求。如果请求是异步的(默认),那么该方法将在请求发送后立即返回
  • setRequestHeader:设置HTTP请求头的值。您必须在open()之后、send()之前调用setRequestHeader()这个方法
  • sendAsBinary:send()方法的变体,用来发送二进制数据

3. 详解Fetch

fetch.jpg Fetch被称为下一代Ajax技术,它采用Promise(Promise是es6的主要内容之一)方式来处理数据。它不是基于XMLHttpRequest构建的高阶语法糖,它比XMLHttpRequest更加简单易用,Fetch是一种简洁明了的API,这种简洁的语法提供了更好的一致性。 Fetch 提供了对 Request 和 Response (以及其他与网络请求有关的)对象的通用定义。使之今后可以被使用到更多地应用场景中:无论是 service worker、Cache API、又或者是其他处理请求和响应的方式,甚至是任何一种需要你自己在程序中生成响应的方式。

  • Fetch示例
  // 发送get请求
  fetch('/server',{method:'GET'})
  // 将返回的数据转换为json对象
  .then(response => response.json())
  // 数据处理逻辑
  .then(res => console.log(res))
  // 异常信息捕获
  .catch(err => console.error(err))
  • 或者使用async/await语法
  async function (){
      try{
          const response = await fetch('/server',{method:'GET'})
          const res = await response.json()
          console.log(res)
      }catch(err){
          console.error(err)
      }
  }

Fetch更清晰、简洁,使之今后可以被使用到更多地应用场景中:无论是 service worker、Cache API、又或者是其他处理请求和响应的方式,甚至是任何一种需要你自己在程序中生成响应的方式。 Fetch API

Headers

属性

  • Headers.append():给现有的header添加一个值, 或者添加一个未存在的header并赋值
  • Headers.delete():从Headers对象中删除指定header
  • Headers.entries():以迭代器的形式返回Headers对象中所有的键值对
  • Headers.get():以ByteString的形式从Headers对象中返回指定header的全部值
  • Headers.has():以布尔值的形式从Headers对象中返回是否存在指定的header
  • Headers.keys():以迭代器的形式返回Headers对象中所有存在的header名
  • Headers.set():替换现有的header的值, 或者添加一个未存在的header并赋值
  • Headers.values():以迭代器的形式返回Headers对象中所有存在的header的值

Request

属性

  • Request.method(只读):包含请求的方法 (GET, POST, 等)
  • Request.url(只读):包含这个请求的URL
  • Request.headers(只读):包含请求相关的Headers对象
  • Request.context (en-US)(只读): 包含请求的上下文(例如:audio, image, iframe, 等)
  • Request.referrer (en-US)(只读):?包含请求的来源 (例如:client)
  • Request.referrerPolicy (en-US)(只读):?包含请求来源的策略 (例如:no-referrer)
  • Request.mode(只读):包含请求的模式 (例如: cors, no-cors, same-origin, navigate)
  • Request.credentials(只读):包含请求的证书(例如: omit, same-origin)
  • Request.redirect (en-US)(只读):包含?如何处理重定向模式,它可能是一个 follow ,error或者manual
  • Request.integrity (en-US)(只读):包含请求的子资源的完整性值 (例如: sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)
  • Request.cache(只读):包含请求的缓存模式 (例如: default, reload, no-cache) Request实现了Body, 所以它还具有以下属性可用:
  • Body.body (en-US)(只读):一个简单getter用于曝光一个ReadableStream的主体内容
  • Body.bodyUsed (en-US)(只读):存储一个Boolean (en-US)判断主体是否已经被用于一个响应中 方法
  • Request.clone():创建当前request的副本 Request Body 接口:
  • Body.arrayBuffer() (en-US):返回解决一个ArrayBuffer表示的请求主体的promise
  • Body.blob() (en-US):返回解决一个Blob表示的请求主体的promise
  • Body.formData() (en-US):返回解决一个FormData表示的请求主体的promise
  • Body.json() (en-US):返回解决一个JSON表示的请求主体的promise
  • Body.text() (en-US):返回解决一个USVString(文本)表示的请求主体的promise 注意:这些Body功能只能运行一次; 随后的调用将通过空strings/ ArrayBuffers解析

Response

属性

  • Response.headers(只读):包含此 Response 所关联的 Headers 对象
  • Response.ok(只读):包含了一个布尔值,标示该 Response 成功(HTTP 状态码的范围在 200-299)
  • Response.redirected(只读):表示该 Response 是否来自一个重定向,如果是的话,它的 URL 列表将会有多个条目
  • Response.status(只读):包含 Response 的状态码 (例如 200 表示成功)
  • Response.statusText(只读):包含了与该 Response 状态码一致的状态信息(例如,OK对应 200)
  • Response.type(只读):包含 Response 的类型(例如,basic、cors)
  • Response.url(只读):包含 Response 的URL
  • Response.useFinalURL:包含了一个布尔值,来标示这是否是该 Response 的最终 URL Response Body 接口:
  • Body.body (en-US)(只读):一个简单的 getter,用于暴露一个 ReadableStream 类型的 body 内容
  • Body.bodyUsed (en-US)(只读):包含了一个布尔值 (en-US)来标示该 Response 是否读取过 Body 方法
  • Response.clone():创建一个 Response 对象的克隆
  • Response.error():返回一个绑定了网络错误的新的 Response 对象
  • Response.redirect():用另一个 URL 创建一个新的 Response Response Body 接口:
  • Body.arrayBuffer() (en-US):读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 ArrayBuffer 格式的 Promise 对象。
  • Body.blob() (en-US):读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 Blob 格式的 Promise 对象。
  • Body.formData() (en-US):读取Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 FormData 格式的 Promise 对象。
  • Body.json() (en-US):读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 JSON 格式的 Promise 对象。
  • Body.text() (en-US):读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 USVString 格式的 Promise 对象。

4. Ajax和Fetch对比

AjaxFetch
是对XMLHttpRequest对象封装的方法全局量window的一个方法
能具体区分请求状态只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
可以监测请求进度无法监测
原生使用繁琐,大多封装使用语法简洁,简单易懂
所有浏览器均支持大多数浏览器支持(如:IE不支持)

5. 结论

  • 多数开发人员选择使用Fetch,由于它简洁的语法,语义化的API,使得Fetch比XMLHttpRequest更有优势。目前来看只有两种特定情况下还需要使用XMLHttpRequest支持:
  1. 非常老的浏览器,如IE。但这个问题随着时间的推移会逐渐改善。
  2. 上传文件时,如需上传的进度回馈,显示上传进度条。Fetch后续将会支持。

6. 参考资料

  • MDN Ajax
  • MDN Fetch
  • MDN XMLHttpRequest