网络请求与远程资源

1. 前言

 在XHR(XMLHttpRequest)出现之前,Ajax(Asynchronous JavaScript + XML,异步JavaScript加XML)风格的通信必须通过一些黑科技实现,主要是使用隐藏帧技术(隐藏框架)或者是内嵌帧技术(内嵌的框架)。Ajax技术能实现:

  • 不刷新页面更新网页
  • 在页面加载后从服务器请求数据
  • 在页面加载后从服务器接收数据
  • 在后台向服务器发送数据

 实际上在Garrett发布关于ajax技术文章之前,这种技术就已经存在了,不过在那之前一般称这种技术为远程脚本。这种浏览器与服务器的通信早在1998年就通过不同的方式实现了。最初,JavaScript对服务器的请求可以通过中介(如Java小程序或flash影片)来发送。后来XHR对象为开发者提供了原生的浏览器通信能力,减少了实现这个目的的工作量。

 XHR为发送服器请求获取响应提供了合理的接口。这个接口可以实现异步从服务器获取额外的数据,跟ajax一样用户可以在不刷新页面的情况下获取新的数据。通过XHR对象获取数据后,可以通过DOM把数据插入网页。

 由于XHR对象的API比较难用,所以在Fetch API诞生后就迅速成为了XHR更现代的替代标准。Fetch API支持promiseservice worker,已经成为极其强大的web开发工具。

 XHR(XMLHttpRequest)实际上是过时web规范的产物,实际开发中尽可能使用Fetch API

2. XMLHttpRequest对象

 IE5是第一个引入XHR对象的浏览器。在 IE5 中,XHR 对象是通过 MSXML 库中的一个 ActiveX

对象实现的。在 IE 中可能会遇到三种不同版本的 XHR 对象,即 MSXML2.XMLHttpMSXML2.XMLHttp.3.0MXSML2.XMLHttp.6.0。所有现代浏览器都通过XMLHttpRequest构造函数原生支持XHR对象:

let xhr = new XMLHttpRequest();
2.1 使用xhr
建立连接

 使用xhr需要调用open()方法,这个方法接收5个参数,其中三个必选参数,两个可选参数

  • 必选参数:
    • 请求类型(比如GET/POST,大小写不敏感)
    • 请求url
    • 表示请求是否是异步的布尔值(默认为true)
  • 可选参数:
    • username:可选参数,如果服务器需要验证,该参数指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。
    • password:可选参数,验证信息中的密码部分,如果用户名为空,则该值将被忽略
xhr.open("GET", "example.php", false);

 上面示例的代码就可以向example.php发送一个同步的GET请求。有关这行代码,需要说明两点:

  • 一是 URL相对于执行代码的当前页面(当然也可以使用绝对路径);
  • 二是调用 open()方法并不会真正发送请求,只是为发送请求做好准备。

注意:只能向同一个域中使用相同端口和协议的 URL 发送请求。如果 URL 与启动请求的页面有任何差别,都会引发安全错误。

发送请求

 建立连接后,可以使用send()发送请求。

xhr.open("GET", "example.php", false);
xhr.send(null);

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

 由于这次请求是同步的,JavaScript 代码会等到服务器响应之后再继续执行。在收到响应后,XHR对象的以下属性会被填充上数据:

​  responseText:作为响应主体被返回的文本。

​  responseXML:如果响应的内容类型是"text/xml""application/xml",这个属性中将 保存包含着响应数据的 XML DOM 文档。

​  status:响应的 HTTP 状态。

​  statusText:HTTP 状态的说明

 收到响应后,第一步就是要检查 status 属性,判断响应是否已经成功返回。一般来说,HTTP状态代码为 2xx 表示成功。此时responseTextresponseXML(如果内容类型正确)属性中会有内容。如果HTTP的状态码为304,则表示资源未被修改过,是从浏览器缓存中直接拿取的。当然,也意味着响应是有效的。为确保接收到适当的响应,应该检查HTTP响应的状态码:

xhr.open("GET", "example.php", false);
xhr.send(null);

// 同步请求时
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
    alert(xhr.responseText);
}else{
    alert("Request was unsuccessful: " + xhr.status);
}

 根据返回的状态代码,这个例子可能会显示由服务器返回的内容,也可能会显示一条错误消息。由于statusText属性在跨浏览器使用时不太靠谱,所以我们需要检查status来决定下一步的操作。另外,无论内容类型是什么,响应主体的内容都会保存到 responseText 属性中;而对于非 XML 数据而言,responseXML 属性的值将为 null。

异步请求的情况

 前面是根据同步请求来介绍XHR对象的属性,但是大多数情况下使用异步请求更好。异步请求可以解决JavaScript代码执行过程中发生阻塞的问题。

 此时可以检测 XHR 对象的 readyState 属性,该属性表示请求/响应过程的当前活动阶段,这个属性可取的值如下:

 0:未初始化。尚未调用 open()方法。

 1:启动。已经调用 open()方法,但尚未调用 send()方法。

 2:发送。已经调用 send()方法,但尚未接收到响应。

 3:接收。已经接收到部分响应数据。

 4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。

 我们可以给XHR对象自定义一个onreadystatechange属性,只要 readyState 属性的值由一个值变成另一个值,都会触发一次 onreadystatechange 事件。利用这个事件来检测每次状态变化后readyState的值。通常只需要判断readyState的值是否为4,因为值为4时所有的数据已经就绪。不过,必须在调用 open()之前指定 onreadystatechange事件处理程序才能确保跨浏览器兼容性。下面来看一个例子:

const cityData = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
    if (xhr.readyState === 4) {
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        }else{
            console.log(xhr.status);
        }
    }
};

xhr.open("get", cityData, true);
xhr.send(null);

responseText保存了响应体返回的文本;cityData保存了JSON格式的数据接口,那么得到的结果就是一堆JSON格式的数据(截图只展示了一部分数据):
在这里插入图片描述
取消异步请求

 在接收到响应之前还可以调用 abort()方法来取消异步请求,如下所示:

xhr.abort();

 调用这个方法后,XHR 对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性。在终止请求之后,还应该对 XHR 对象进行解引用操作。由于内存原因,不建议重用 XHR 对象。

2.1.1 总结
createXHR()函数

 为了实现兼容性,比如兼容IE早期版本,我们可以封装一个createXHR()函数来使用XHR对象

function createXHR(){
    if (typeof XMLHttpRequest != "undefined") {
        return new XMLHttpRequest();
    }else if(typeof ActiveXObject != "undefined"){
        if(typeof arguments.callee.activeString != "string"){
            let versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i, len;
            let len = versions.lengthl;
            for (let i = 0; i < len; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeString = versions[i];
                } catch (ex) {
                    // 跳过
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeString);
    }else{
        throw new Error("No XHR object available.");
    }
}
发送同步请求代码
var xhr = new XMLHttpRequest();
xhr.open("get", cityData, true);
xhr.send(null);
if (xhr.readyState === 4) {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        console.log(xhr.responseText);
    }else{
        console.log(xhr.status);
    }
}
发送异步请求代码
var xhr = new XMLHttpRequest();
xhr.onload = function(){
    if (xhr.readyState === 4) {
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        }else{
            console.log(xhr.status);
        }
    }
};

xhr.open("get", cityData, true);
xhr.send(null);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值