美文网首页
Ajax的核心---XMLHttpRequest

Ajax的核心---XMLHttpRequest

作者: BigDipper | 来源:发表于2020-07-17 15:12 被阅读0次

    ⛽ 概述

    XMLHttpRequest对象的作用是与服务器交互,在不刷新页面的情况下请求特定 URL,获取数据。其实说白了,就是用异步的方式,向服务端发起请求,获取或提交数据,如果非要用通俗又粗俗的话说,它就是用来发ajax请求的

    • 其实XMLHttpRequest对象也能发送同步请求,只要把open()方法的第三个参数设置成false,就行。
      只是由于同步会对用户体验产生负面影响,浏览器都已经把在主线程上的同步请求弃用了。所以,只要记住XMLHttpRequest对象只能发送异步请求就对了。

    • XMLHttpRequest的通信流程是由客户端发起的。
      如果需要由服务端发起向客户端的推送,可以用server sent events这里是大神的教程)。
      如果需要全双工的通信,WebSocket这里还是大神的教程)可能是更好的选择。

    🚥 运转流程

    首先,先创建一个XMLHttpRequest实例:

    const XHR = new XMLHttpRequest();
    

    然后,调用open()方法:

    const XHR = new XMLHttpRequest();
    
    XHR.open('GET', '/apis');
    

    这行代码会启动一个针对/apis地址(也可以是绝对路径)的GET请求,要说明的是:调用open()方法并不会真正发送请求,而只是启动一个请求以备发送。

    要发送真正的请求,必须像下面这样调用send()方法:

    const XHR = new XMLHttpRequest();
    
    XHR.open('GET', '/apis');
    XHR.send(null);
    

    这里的send()方法接收一个参数,既要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null,因为这个参数对有些浏览器来说是必需的。

    因为我们发送的是异步请求,所以要依据readyState属性的变化,来了解异步的过程。该属性表示请求/响应过程的当前活动阶段,如图所示:

    readyState属性的变化.png

    只要readyState属性的值由一个值变成另一个值,都会触发一次readystatechange事件。通常,我们只对readyState值为4的阶段感兴趣,因为这时所有数据都已经就绪。不过,必须在调用open()之前指定onreadystatechange事件处理程序才能确保跨浏览器兼容性。

    const XHR = new XMLHttpRequest();
    
    XHR.onreadystatechange = () => {
        if (XHR.readyState === 4) {
            // blah blah blah...
        } else { }
    }
    
    XHR.open('GET', '/apis');
    XHR.send(null);
    

    服务端接收到请求信息,并做出响应(如果通信正常),客户端在收到响应后,响应的数据会自动填充XHR对象的属性,比如:status(响应的HTTP状态)、responseText(作为响应主体被返回的文本)......。

    一般来说,可以将HTTP状态码为200作为成功的标志。此外,状态代码为304表示请求的资源并没有被修改,可以直接使用浏览器中缓存的版本,当然,也意味着响应是有效的。

    const XHR = new XMLHttpRequest();
    
    XHR.onreadystatechange = () => {
        if (XHR.readyState === 4) {
            if (XHR.status < 300 || XHR.status === 304) {
                // blah blah blah...
            } else {
                alert(`请求失败了:${XHR.status}`);
            }
        } else { }
    }
    
    XHR.open('GET', '/apis');
    XHR.send(null);
    

    此处要说明一下:状态码为304的意思是说浏览器可以用缓存的版本,但是不代表浏览器没有向服务端发起请求。其实,浏览器是真真正正地向服务端发了请求的,只是服务端验证了资源的过期时间后,仅仅是告诉浏览器:“我就不给你数据了,你用你自己那份资源吧,还在保质期呢。”

    另外,在接收到响应之前还可以调用abort()方法,来取消异步请求。在终止请求后,由于内存原因,还应该对XHR对象进行解引用操作。

    上面,是使用XMLHttpRequest的最简单流程。

    下面,再补充一些细节操作:

    • 添加/获取头部信息

    每个HTTP请求和响应都会带有相应的头部信息(有的有用,有的对开发人员没用),XMLHttpRequest对象也提供了操作这两种头部(即请求头部和响应头部)信息的方法。

    XMLHttpRequestsetRequestHeader()方法可以设置自定义的请求头部信息,要成功发送请求头部信息,此方法必须在调用open()方法之后且调用send()方法之前调用。

    调用XMLHttpRequestgetResponseHeader()方法并传入头部字段名称,可以取得相应的响应头部信息。而调用getAllResponseHeaders()方法则可以取得一个包含所用头部信息的长字符串。这两个方法也要在调用open()方法之后且调用send()方法之前调用。

    const XHR = new XMLHttpRequest();
    
    XHR.onreadystatechange = () => {
        if (XHR.readyState === 4) {
            if (XHR.status < 300 || XHR.status === 304) {
                // blah blah blah...
            } else {
                alert(`请求失败了:${XHR.status}`);
            }
        } else { }
    }
    
    XHR.open('GET', '/apis');
    
    XHR.setRequestHeader('请求头的自定义字段名称', '头部字段的值');
    XHR.getResponseHeader('响应头的字段名称');
    XHR.getAllResponseHeaders();
    
    XHR.send(null);
    

    为什么要添加/获取头信息呢?

    此之目的仅仅是为了方便信息的传输。服务器在接收到自定义的头部信息后,可以执行相应的后续操作;同样的,在服务端,也可以利用头部信息向浏览器发送额外的、结构化的数据。而且,不是每次请求都必须做这种操作,要看使用场景,需要用的时候再用。

    • 监测进度

    XMLHttpRequest提供了各种在请求被处理期间发生的事件以供监听。这包括定期进度通知、 错误通知,等等。

    const XHR = new XMLHttpRequest();
    
    /**
     * 监听请求过程中的各种事件
     */
    
    XHR.onprogress = (oEvent) => {
        // 服务端到客户端的传输进程(下载)
        // 此时readyState=2
    
        if (oEvent.lengthComputable) {
            const percentComplete = oEvent.loaded / oEvent.total * 100;
    
            // blah blah blah...
        } else {
        // 总大小未知时不能计算进程信息
        }
    };
    
    XHR.onload = (oEvent) => {
        // 传输完成,所有数据保存在response中。
        // 此时readyState=4
    
        // blah blah blah...
    };
    
    XHR.onerror = (oEvent) => {
        // 当request遭遇错误时触发。
    
        // blah blah blah...
    };
    
    XHR.onabort= (oEvent) => {
        // 当request被停止时触发。
    
        // blah blah blah...
    };
    
    XHR.onreadystatechange = () => {
        if (XHR.readyState === 4) {
            if (XHR.status < 300 || XHR.status === 304) {
                // blah blah blah...
            } else {
                alert(`请求失败了:${XHR.status}`);
            }
        } else { }
    }
    
    XHR.open('GET', '/a.txt');
    XHR.send(null);
    

    你需要在请求调用open()之前添加事件监听。否则progress事件将不会被触发。

    progress事件同时存在于下载和上传的传输。上面的例子是针对下载的相关事件。上传相关事件在 XMLHttpRequest.upload对象上被触发,像下面这样:

    var oReq = new XMLHttpRequest();
    
    oReq.upload.addEventListener("progress", updateProgress);
    oReq.upload.addEventListener("load" , transferComplete);
    oReq.upload.addEventListener("error", transferFailed  );
    oReq.upload.addEventListener("abort", transferCanceled);
    
    oReq.open();
    

    🚗 示例

    • 🧪 发送一个GET请求
    const XHR = new XMLHttpRequest();
    
    XHR.onreadystatechange = () => {
        if (XHR.readyState === 4) {
            if (XHR.status < 300 || XHR.status === 304) {
                console.dir(XHR.response);
            } else { }
        } else { }
    }
    
    XHR.open('GET', '/apis');
    XHR.send(null);
    

    结果如图所示:


    没有参数的GET请求.png

    给请求加点参数:

    XHR.open("GET", "/apis?ab=13&page=1");
    XHR.setRequestHeader('CC', 989);
    XHR.send(null);
    

    结果如图所示:


    有参数的GET请求.png
    • 🧪 发送一个POST请求
    const XHR = new XMLHttpRequest();
    
    XHR.onreadystatechange = () => {
        if (XHR.readyState === 4) {
            if (XHR.status < 300 || XHR.status === 304) {
                console.dir(XHR.response);
            } else { }
        } else { }
    }
    
    XHR.open('POST', '/apis');
    XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    XHR.send('name=张三丰&职务=掌门');
    

    结果如图所示:


    POST请求.png

    为什么要添加Content-Type请求头信息呢?

    需要设置Content-Type头信息,完全是由于服务端的原因。因为无论是表单、Ajax、jsonp,到了服务端那儿,它只认一种请求,就是form表单请求,所以Ajax发送的POST请求,服务端会认成是form表单的POST请求,而form表单发送POST请求时,如果不设置enctype属性,就会以默认的application/x-www-form-urlencoded方式提交表单,因此不加不行啊。

    如果要上传文件,需要将Content-Type头的值设置为multipart/form-data,总之,跟form表单的提交方式保持一致就对了。

    这篇《Express.js 解析 Post 数据类型的正确姿势》介绍了POST请求的四种方式,值得一看👏。

    好了,先写到这儿吧。

    --(完)--

    相关文章

      网友评论

          本文标题:Ajax的核心---XMLHttpRequest

          本文链接:https://www.haomeiwen.com/subject/scjfhktx.html