你真的了解XMLHttpRequest么?

说起浏览器和服务器的通信,最先想到的技术必然就是Ajax了。在ES6中还提供了fetch API,相当于把Ajax用Promise封装起来,看起来更像同步操作了。先大概看下这两种方式:

  • ES5中的Ajxa
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
    if (xhr.readyState !== 4) {
        return;
    }
    if (xhr.status >= 200 && xhr.status <= 300 || xhr.status === 304) {
        alter(xhr.response);
    }
}
xhr.open('GET', url, true);
xhr.send();

  • ES6中fetch
fetch(url)
    .then(response => alter(response))
    .catch(e => alert(e));

很明显fetch方法看起来更加简洁,而且fetch也有很多自己很好的特性。想仔细了解fetch的童鞋可以去看看阮一峰老师的博客:Fetch API教程(ps:前端不识阮一峰,学遍技术也枉然)。本篇文章主要是讲ES5中Ajax技术的核心XMLHttpRequst对象的用法(下文都把XMLHttpRequest对象称为XHR对象)以及XHR对象的一些属性。

第一步:new XHR对象

既然要使用XHR对象,当然要先new一个XHR对象了

// new一个XHR对象
let xhr = new XMLHttpRequest();

tip: 对于IE7之前的浏览器会有兼容问题,但我觉得如果现在还有人用IE7之前的浏览器,他也不配用2021年的程序员写的网页。

第二步:为onreadystatechange事件绑定事件处理函数

先说下XHR的readyState属性,该属性表示请求/响应过程的当前阶段,也就是请求/响应事件大概进行到什么程度了。该属性有5个取值:

  • 0:未初始化。尚未调用open()方法(下文会介绍)。
  • 1:启动。已经调用open()方法,但未调用send()方法(下文会介绍)。
  • 2:发送。已经调用send()方法,但未收到响应。
  • 3:接收。已经接收到部分信息。
  • 4:完成。已经接收到所有信息。

只要readyState的值改变一次,就会触发onreadystatechange事件。也就是说,其实一次的请求/响应会触发4次onreadystatechange事件,但通常情况我们只对完成阶段感兴趣,也就是readyState为4的阶段。然后再说下XHR的另一个属性status。这个属性的值为http请求的响应码,没错,就是那些什么200啊,304啊,404啊,500什么的。还有一个responseText属性,这个属性作为响应主体被返回的文本。明确了这几个概念,就可以为XHR对象的onreadystatechange事件绑定事件响应函数了。

// new一个XHR对象
let xhr = new XMLHttpRequest();
// 为onreadystatechange事件绑定事件响应函数
xhr.onreadystatechange = function () {
    // 只有readyState为4的时候我们才处理返回的数据
    if (xhr.readyState !== 4) {
        return;
    }
    // 根据status属性(状态码)来决定下一步操作
    if (xhr.status >= 200 && xhr.status <= 300 || xhr.status === 304) {
        alter(xhr.responseText);
    }
};

第三步:调用open()方法

当为onreadystatechange事件绑定事件响应函数后,就要发起请求了。与发起请求有关的第一个函数就是open()。这个函数接收三个参数:

  • 请求方式(String类型):'GET'或者其他http请求方式
  • url(String类型):请求的地址
  • 是否异步处理请求(Boolean类型):如果为true,则在请求的时候继续处理其余脚本(异步)。如果为false,则等请求/响应完成后再处理剩余脚本(同步)。

但要明确一点,调用open()方法以后并没有真正发送请求,而是做好了发送请求的准备。

// new一个XHR对象
let xhr = new XMLHttpRequest();
// 为onreadystatechange事件绑定事件响应函数
xhr.onreadystatechange = function () {
    // 只有readyState为4的时候我们才处理返回的数据
    if (xhr.readyState !== 4) {
        return;
    }
    // 根据status属性(状态码)来决定下一步操作
    if (xhr.status >= 200 && xhr.status <= 300 || xhr.status === 304) {
        alter(xhr.responseText);
    }
};
// 调用open()方法
xhr.open('GET', url, true);

这个时候大军已经通过open()方法做好出征的准备了,粮草装备什么的都齐全了,就等总指挥下令了,也就是调用send()方法。

第四步:调用send()方法

send()方法就是用来真正发送一个请求。send()方法接收一个对象参数,这个参数就是作为请求体一起发送给服务器(POST方法和GET方法的区别)。如果是使用get请求方式这里直接为空也可以,但是为了兼容有些浏览器最好传一个null。

// new一个XHR对象
let xhr = new XMLHttpRequest();
// 为onreadystatechange事件绑定事件响应函数
xhr.onreadystatechange = function () {
    // 只有readyState为4的时候我们才处理返回的数据
    if (xhr.readyState !== 4) {
        return;
    }
    // 根据status属性(状态码)来决定下一步操作
    if (xhr.status >= 200 && xhr.status <= 300 || xhr.status === 304) {
        alter(xhr.responseText);
    }
};
// 调用open()方法
xhr.open('GET', url, true);
// 调用send()方法
xhr.send(null);

到此为止,一个XHR对象的Ajax的基本步骤就做完了,至于设置请求头和收到响应后的其他方法,这里就不多做介绍了,最后放一个封装的Ajax函数来结束本篇文章吧

// es5方式
let ajax = {
    get: function (url, fn, bool) {
        let xhr = new XMLHttpRequest();
        // 先绑定事件再调用open方法可以做到向后兼容
        xhr.onreadystatechange = handle();
        // oepn后并未实际发出请求,而是做一个请求准备
        xhr.open('GET', url, bool = true);
        xhr.send();

        // 每次readyStated的值改变都会触发onreadystatechange事件
        function handle() {
            // 不为4的时候数据没有全部接收,不能使用
            if (xhr.readyState !== 4) {
                return;
            }
            //200:成功获取数据;304:数据没有改变,使用缓存
            if (xhr.status === 200 || xhr.status === 304) {
                fn.call(xhr.response);
            }
        }
    },

    post: function (url, fn, bool) {
        let xhr = new XMLHttpRequest();
        xhr.onreadystatechange = handle();
        xhr.open('POST', url, bool);
        xhr.send();

        function handle() {
            if (xhr.readystate !== 400) {
                return;
            }
            if (xhr.status === 200 || xhr.status === 304) {
                fn.call(response);
            }
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容