合抱之木生于毫末,九层之台起于累土,千里之行始于足下

手写一个Ajax请求


手写一个Ajax请求

前面我们讲到了XMLHttpRequest的属性、方法和事件。也聊到了很多纯原生写法上需要注意的点,这是我们使用第三方库时常常忽略的。可是,说了再多也不如亲手试试,今天我们就来使用纯原生的方式写一个Ajax请求。

首先说明,由于DOM 2级的扩展,Ajax请求监听方式多了很多方式,我们今天实现的是目前现代浏览器都能支持的方式,不考虑较早版本的兼容问题。所以这里我讲主要使用以下几个属性、方法和事件。

属性

方法

事件

第一步,创建一个XHR实例

编写Ajax的第一步自然是需要创建一个XHR实例。

// 创建一个xhr实例
var xhr = new XMLHttpRequest()

第二步,绑定XHR相关的事件

为了尽可能的兼容所有浏览器,最好将事件绑定放在open方法之前。

xhr.onloadstart = function () {
  // 当数据开始下载的第一刻,触发该事件
}

xhr.onprogress = function() {
  // 当数据开始下载时,反复触发该事件
  // 由于TCP请求会造成切片分包发送的情况,每个分包下载完成时都会触发该事件
  // 如果数据量较小,则不会存在切片分包情况,该事件将只会触发一次
}

xhr.onload = function () {
  // 如果所有分片数据都接收完成,触发该事件
}

xhr.onloadend = function () {
  // 请求完成时(无论成功还是失败),触发该事件
}

xhr.onerror = function () {
  // 当请求发生错误时,触发该事件
}

xhr.onabort = function () {
  // 当请求被主动取消时,触发该事件
}

xhr.ontimeout = function () {
  // 如果xhr实例设置了timeout,且请求时间超过了timeout设置的超时时间,触发该事件
}

第三步,准备请求

调用open方法后,发送请求已经做好了准备,这时候随时可以执行发送操作

// 包含三个参数:请求的方法、请求的url(可以是相对的,也可以是绝对的)、是否异步
xhr.open('GET', 'https://w2.test.com/api/test/index', true)

第四步,设置额外配置

在open方法执行之后,我们可以对xhr对象设置属性、添加请求头以及强制设置返回的Content-Type

// 请求超过20秒未响应之后,执行超时操作
xhr.timeout = 20000
// 根据该属性可以帮助response属性正确解析响应内容
xhr.responseType = "blob"
// 可以向跨域请求发送Cookies、额外的请求头或者TLS客户端证书等信息
xhr.withCredentials = true
// 设置额外的请求头
xhr.setRequestHeader('x-token', 'authorize-token')
// 强制设定MIME,帮助浏览器更好地解析响应数据
xhr.overrideMimeType('text/plain')

第五步,发送请求

在这些操作全部完成之后,可以调用send方法发送请求。

// 如果是POST请求,send方法的第一个参数为要发送的载荷
// 如果是GET请求,第一个参数传null(为了兼容所有浏览器)
xhr.send(null)

完整例子

var xhr = new XMLHttpRequest()

xhr.onloadstart = function () {
  // 当数据开始下载的第一刻,触发该事件
}

xhr.onprogress = function() {
  // 当数据开始下载时,反复触发该事件
  // 由于TCP请求会造成切片分包发送的情况,每个分包下载完成时都会触发该事件
  // 如果数据量较小,则不会存在切片分包情况,该事件将只会触发一次
}

xhr.onload = function () {
  // 如果所有分片数据都接收完成,触发该事件
  // 在这里可以调用getAllResponseHeaders方法来获取请求头字符串
  var headers = xhr.getAllResponseHeaders()
  var arr = headers.trim().split(/[\r\n]+/);
  var headerMap = {};

  arr.forEach(function (line) {
    var parts = line.split(': ');
    var header = parts.shift();
    var value = parts.join(': ');
    headerMap[header] = value;
  });
}

xhr.onloadend = function () {
  // 请求完成时(无论成功还是失败),触发该事件
}

xhr.onerror = function () {
  // 当请求发生错误时,触发该事件
}

xhr.onabort = function () {
  // 当请求被主动取消时,触发该事件
}

xhr.ontimeout = function () {
  // 如果xhr实例设置了timeout,且请求时间超过了timeout设置的超时时间,触发该事件
}

xhr.open('GET', 'https://w2.test.com/api/test/index', true)
xhr.timeout = 20000
xhr.responseType = "blob"
xhr.withCredentials = true
xhr.setRequestHeader('x-token', 'authorize-token')
xhr.overrideMimeType('text/plain')
xhr.send(null)