使用 XMLHttpRequest(XHR) 对象可以与服务器交互。您可以从 URL 获取数据,而无需让整个的页面刷新。
Ajax(Asynchronous JavaScript and XML)是一系列 Web 开发技术的集合,使用很多的 Web 技术在客户端开发异步 Web 应用。利用 Ajax,Web 应用可以异步的发送数据获取数据,而不干扰现有页面的显示和行为。通过解耦数据接口层和展现层,Ajax 允许 Web 页面或者其他扩展的 Web 应用动态的改变数据而不用重新加载整个页面。实现通常选择 JSON 代替 XML,因为更接近 JavaScript。
EventTarget <- XMLHttpRequestEventTarget <- XMLHttpRequest
const xhr = new XMLHttpRequest();
此接口继承了 XMLHttpRequestEventTarget 和 EventTarget 的属性。
| 属性 | 说明 | 类型 |
|---|---|---|
onreadystatechange | 当 readyState 属性发生改变时,设定的回调函数会被调用 | Function |
readyState | (只读) 用于表示请求的五种状态 | unsigned short |
response | (只读) 用于获取整个响应实体,响应体的类型由 responseType 来指定 | Blob ArrayBuffer Document JSON String null(请求未完成或失败) |
responseText | (只读) 用于获取请求的响应文本 | DOMString null(请求未完成或失败) |
responseType | 用于设置该值能够改变响应类型 | XMLHttpRequestResponseType |
status | (只读)用于获取请求的 响应状态码 | unsigned short |
statusText | (只读)用于获取请求的 响应状态信息,包含一个状态码和消息文本 | DOMString |
timeout | 用于表示请求 最大请求时间(毫秒),若超出该时间,请求会自动终止 | unsigned long |
upload | (只读) 用于在 upload 上添加一个 事件监听 来跟踪上传过程 | XMLHttpRequestUpload |
withCredentials | 用于指定跨域 Access-Control 请求是否应当带有授权信息,如 Cookie 或授权首部字段 | Boolean |
使用示例:
const xhr = new XMLHttpRequest();xhr.open('GET', 'https://developer.mozilla.org/', true);xhr.onreadystatechange = function() {if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {console.log(xhr.responseText);}};xhr.send();
| 值 | 状态 | 描述 |
|---|---|---|
| 0 | UNSENT | 请求代表被创建,但尚未调用 open 方法 |
| 1 | OPENED | open 方法已经被调用 |
| 2 | HEADERS_RECEIVED | send 方法已被调用,并且头部和状态已经可访问 |
| 3 | LOADING | 下载中(responseText 属性已经包含部分数据) |
| 4 | DONE | 下载操作已完成 |
下列原型方法按照请求的生命周期设定:
XMLHttpRequest.open 方法用于初始化一个请求,。
语法:
xhr.open(method, url);xhr.open(method, url, async);xhr.open(method, url, async, user);xhr.open(method, url, async, user, password);
参数:
method:请求方法,如 GET、POST、PUT、DELETEurl:请求的 URL 地址async:一个可选的布尔值参数,默认值为 true,表示执行异步操作。如果值为 false,则 send 方法不会返回任何东西,直到接收到了服务器的返回数据user:用户名(可选参数),用于授权。默认参数为空字符串password:密码(可选参数),用于授权。默认参数为空字符串使用方法:
const xhr = new XMLHttpRequest();xhr.open('GET', 'https://example.com/user');
注意事项:
method 不是有效的 HTTP 方法或 URL 地址不能被成功解析,将会抛出 SyntaxError 异常CONNECT、TRACE 或 TRACK 将会抛出 SecurityError 异常XMLHttpRequest.sentRequestHeader 方法用于设置 HTTP 请求头信息。
⚠️ 注意:在这之前,你必须确认已经调用了
open方法打开了一个 URL
语法:
xhr.setRequestHeader(header, value);
参数:
header:请求头名称value:请求头值XMLHttpRequest.overrideMimeType 方法用于重写由服务器返回的 MIME 类型,使服务端响应信息中传输的数据按照指定 MIME 类型处理。
例如,可以用于强制把响应流当做 text/xml 来解析,即使服务器没有指明数据是这个类型。
⚠️ 注意:这个方法必须在
send之前被调用
XMLHttpRequest.send 方法用于发起网络请求。
⚠️ 注意:所有相关的事件绑定必须在调用
send方法之前进行
语法:
xhr.send(body);
参数:
body:在 XHR 请求中要发送的数据体,可以是以下其中某种类型使用方法:
const xhr = new XMLHttpRequest();xhr.open('GET', '/server', true);xhr.onload = function() {// 请求结束后处理};xhr.send(null);// xhr.send('Hello world!')// xhr.send(new Blob())// xhr.send(new Int8Array())// xhr.send({ form: 'data' })// xhr.send(document)
XMLHttpRequest.abort 用于当请求已发送后立刻中止请求。当该方法执行后,readyState 将会被置为 XMLHttpRequest.UNSENT,并且请求的 status 属性置为 0。
XMLHttpRequest.getRequestHeader 方法用于获取指定响应头的值,如果响应头还没有被接收,或该响应头不存在,则返回 null。
注意:使用该方法获取某些响应头时,浏览器会抛出异常,具体原因如下
response 中的 Set-Cookie、Set-Cookie2 这 2 个字段,无论是同域还是跨域请求XMLHttpRequest.getAllResquestHeaders 方法用于获取所有响应头信息(响应头名和值),如果响应头还没有接收,则返回 null。
⚠️ 注意:使用该方法获取的响应头首部字段与在开发者工具 Network 面板中看到的响应头不一致。
| 事件名 | 说明 |
|---|---|
loadstart | 用于当网络请求发送后触发,即调用 XMLHttpRequest.send 方法后触发,若未被调用则不会触发 |
load | 用于当请求完成时触发,此时 readyState 值为 DONE(4) |
loaded | 用于当某个资源的加载进度停止后触发,例如已经触发 abort、error、load 事件之后 |
progress | 用于当请求接收到数据的时候被周期性触发。 |
abort | 用于当请求被暂停时触发。 |
timeout | 用于当请求超出最大时间时触发。 |
error | 用于当请求遭遇异常时触发 |
事件执行顺序:
readystatechange:readyState 状态变更时触发xhr.loadstartxhr.upload.onloadstartxhr.upload.onprogressxhr.upload.onloadxhr.upload.onloadendxhr.onprogressxhr.onloadxhr.onloadendxhr.onabortxhr.timeoutxhr.errorXMLHttpRequest 对象传送数据的时,通过 progress 事件可以获取传输进度信息。
它分成上传和下载两种情况:
progress 事件属于 XMLHttpRequest 对象progress 事件属于 XMLHttpRequest.upload 对象我们先定义 progress 事件的回调函数:
function updateProgress(event) {if (event.lengthComputable) {var percentComplete = event.loaded / event.total;}}xhr.onprogress = updateProgress;xhr.upload.onprogress = updateProgress;
上面的代码中,event.total 是需要传输的总字节,event.loaded 是已经传输的字节。如果 event.lengthComputable 不为真,则 event.total 等于 0。
定时轮询的基本思路就是浏览器每隔一段时间向浏览器发送 HTTP 请求,服务器端在收到请求后,不论是否有数据更新,都直接进行响应。这种方式实现的即时通信,本质上还是浏览器发送请求,服务器接受请求的一个过程,通过让客户端不断的进行请求,使得客户端能够模拟实时地收到服务器端的数据的变化。
这种方式的优点是比较简单,易于理解,实现起来也没有什么技术难点。缺点是显而易见的,这种方式由于需要不断的建立 HTTP 连接,严重浪费了服务器端和客户端的资源。尤其是在客户端,距离来说,如果有数量级想对比较大的人同时位于基于短轮询的应用中,那么每一个用户的客户端都会疯狂的向服务器端发送 HTTP 请求,而且不会间断。人数越多,服务器端压力越大,这是很不合理的。
因此短轮询不适用于那些同时在线用户数量比较大,并且很注重性能的 Web 应用。
const xhr = new XMLHttpRequest();// 每 1000 毫秒向服务器发送一次轮询请求setInterval(function() {xhr.open('GET', '/server');xhr.onreadystatechange = function() {};xhr.send();}, 1000);
当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 。 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
长轮询和短轮询比起来,明显减少了很多不必要的 http 请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。
function checkUpdate() {var xhr = new XMLHttpRequest();xhr.open('GET', '/user');xhr.onreadystatechange = function() {checkUpdate();};xhr.send();}
实现思路:
轮询与长轮询都是基于 HTTP 的,两者本身存在着缺陷:
两者都是 被动型服务器 的体现:服务器不会主动推送信息,而是在客户端发送 AJAX 请求后进行返回的响应。而理想的模型是在服务器端数据有了变化后,可以主动推送给客户端,这种 主动型 服务器是解决这类问题的很好的方案。Web Sockets 就是这样的方案。
那么长轮询总是比定期轮询更好的选择?
除非消息到达率已知且不变,否则长轮询将始终提供更短的消息延迟。
另一方面,开销讨论需要更细微的观点。首先,请注意,每个传递的消息仍然引起相同的 HTTP 开销;每个新消息都是独立的 HTTP 请求。但是,如果消息到达率高,那么长时间轮询会比定期轮询发出更多的 XHR 请求!
长轮询通过最小化消息延迟来动态地适应消息到达速率,这是您可能想要的或可能不需要的行为。如果对消息延迟要求不高的话,则定时轮询可能是更有效的传输方式。例如,如果消息更新速率较高,则定时轮询提供简单的”消息聚合“机制(即合并一定时间内的消息),这可以减少请求数量并提高移动设备的电池寿命。
四种前端即时通讯技术比较:
短轮询 > 长轮询 > 长连接 SSE > WebSocketWebSocket > 长连接 SSE > 长轮询 > 短轮询参考资料: