uncategorized

XMLHttpRequest 初步

历史

要说 XMLHttpRequest 还要从 Ajax 说起

Ajax 全称Asynchronous Javascript and XML, 由 Jesse James Garrett 提出,是一套综合了多项技术的浏览器端网页开发技术,很多人可能将 Ajax 与 客户端的异步请求画上了约等号,但是其实并不是,其中包含的常用技术就有:

  1. 运用XHTML+CSS来表达信息;
  2. 运用JavaScript操作DOM(Document Object Model)来运行动态效果;
  3. 运用XML和XSLT操作数据
  4. 运用XMLHttpRequest或新的Fetch API与网页服务器进行异步数据交换;

所以我们日常想通过 Ajax请求 这个词表达的多数情况下是最后一项,异步数据交换技术.

XMLHttpRequest 是一个封装好的用于异步请求数据的对象,其上定义了相关的请求方法以及和请求、响应相关的属性. 很多成熟的库 (如jq) 都封装了使用简便的异步请求数据的方法,其底层也大多都是 XMLHttpReqest, 所以理解这个对象以及原生的发送异步请求的方式对于我们理解其他库或者开发自己的相应的库来说都至关重要.

在写下这篇博客的时候(2018-02-02),Fetch API 还是一个比较新的 API,比起 XMLHttpRequest 也更加迷人,这会在稍后的博客中做出解释,本文暂不赘述.

基础

我们可以先从一个比较高的视图上来理解这个对象的使用

  1. 首先我们创建 xhr 对象(即XMLHttpRequest对象,但在IE低版本中不是这个对象,功能类似,故 xhr 在本文一律指代类似对象)
  2. 然后我们使用对象上定义的方法配置我们的请求参数,然后调用相关方法发送
  3. 请求完成后,对象上的相应属性会被写入请求的结果值,此时我们去读取即可
  4. 对象上还定义了一些事件发生的hook,我们为其添加相应的事件回调函数就能在相应事件发生的时机做出响应

整体流程大概如此

详解

基本流程

首先从处理顺序,我们会先创建一个浏览器封装了发送异步请求的方法和属性集合的对象XMLHttpRequest,然后调用open方法来初始化一个请求

1
let xhr = new XMLHttpRequest()

创建对象这步针对IE需要处理一下兼容性, 这篇文章的其中一个部分简要解释了 IE 对于这个对象存在的兼容性问题和解决方案.

初始化请求主要是一些初始参数的传递

1
xhr.open('GET', 'http://apihub.com/weather')

在这里,这个 open 方法的详细定义是:

1
2
3
4
5
6
7
void open(
DOMString method,
DOMString url,
optional boolean async,
optional DOMString user,
optional DOMString password
);

可见,我们只使用了最基本的 method, url 两个必要字段,分别指定了请求方法和请求的地址.

async 字段表示是否用异步的方式发送请求,这里默认为 true,也就是使用异步发送(避免同步).

最后 userpassword 用于授权的时候使用,多数情况下不需要.

初始化好一个连接之后,我们就可以 发射 了,即调用 xhr 对象的 send() 方法

1
xhr.send()

send 完成之后就可以坐等请求完成并且返回了,问题在于,我们并不知道请求什么时候能够返回,一遍一遍的轮询代表请求响应的相关属性未免太傻(当然这在大多数情况下是不可能的),所以我们这里可以利用 xhr 对象上的事件回调来把握请求返回的时机.

xhr 对象上有一个 onreadystatechange 属性,其类型是一个 Function, 也就是 readyState 改变的时候回触发的回调函数,说到这里,自然就引出 readyState 又是个什么东西?

readyState 是一组定义好的状态码,代表了当前请求的进度和状态,其值代表的含义如下:

含义 xhr对象上定义的状态
0 open方法还没有被调用 UNSENT
1 open方法已经被调用 OPENED
2 send方法已经被调用,响应头和响应状态已经返回 HEADERS_RECEIVED
3 响应体下载中 LOADING
4 整个请求过程已经完毕 DONE

注:XMLHttpRequest.DONE === 4 以此类推

参考以上我们就可以知道什么时候请求能够返回,即我们可以定义 onreadystatechange 属性,在该函数中判断 xhr 的当前 readyState,如果为4就是请求已经结束,我们可以进行下一步操作

至此,我们已经可以完成一些简单的请求了,参考整体代码:

1
2
3
4
5
6
7
8
9
let xhr = new XMLHttpRequest()
// 上述创建,如果需要考虑兼容性的话,参考以下链接文章里的兼容性创建方法
xhr.open('GET', 'http://apihub.com/weather')
xhr.onreadystatechange = ()=>{
if (xhr.readyState === XMLHttpRequest.DONE){
// 执行下一步操作
}
}
xhr.send()

兼容性创建

注:此处对于异步模式来说,send 方法调用后会同步地立即返回,然后请求完成后该对象的相应属性会被赋值,并且一些事件会被异步地触发,此即为异步. 如果上面的 async 字段被设置为 false, 那么send 方法会被阻塞直到请求返回,同样的,此时 xhr 对象上的相应属性会被赋值,但此时 onreadystatechange 代表的回调函数始终不会被调用.

携带request body

在使用POST等请求方法的时候,我们可能会需要携带请求 body 传参,在send的时候传入即可

1
2
3
4
xhr.send("MY BODY DATA")
// OR
const data = { "username": "LiLei" }
xhr.send(JSON.stringify(data))

注意: 🚧 通常我们传入的 JSON 类型的数据需要序列化为字符串类型, 根据MDN XMLHttpRequest.send, send 方法支持的参数类型有:ArrayBuffer / ArrayBufferView / Blob / Document / DOMString / FormData几种,而DOMString在 js 中就是普通 String,JSON数据使用的就是这个类型来向服务器发送数据,服务器在接到数据后自行(或由框架处理)来讲符合JSON规范的String参数解析成相应的对象来使用, 所以直接发送js对象是行不通的

设置请求头

设置请求头的需求非常常规,例如后端服务要求在请求的时候要带上登录后颁发的 access_token, 这样的需求大多数时候依赖的是 Authorization 头部字段

1
xhr.setRequestHeader('Authorization', 'Bearer<Some-Token>')

◉ End.


参考资料:

  1. Ajax - wikipedia
  2. XMLHttpRequest - MDN

如博文有叙述不妥以及不准确的地方, 望各位看客不吝赐教, 感谢.

Share