本文详细介绍了 XMLHttpRequest 相关知识,涉及内容:javascript
AJAX即“Asynchronous JavaScript and XML”(异步的JavaScript与XML技术),指的是一套综合了多项技术的浏览器端网页开发技术。Ajax的概念由杰西·詹姆士·贾瑞特所提出。 php
传统的Web应用容许用户端填写表单(form),当提交表单时就向网页服务器发送一个请求。服务器接收并处理传来的表单,而后送回一个新的网页,但这个作法浪费了许多带宽,由于在先后两个页面中的大部分HTML码每每是相同的。因为每次应用的沟通都须要向服务器发送请求,应用的回应时间依赖于服务器的回应时间。这致使了用户界面的回应比本机应用慢得多。html
与此不一样,AJAX应用能够仅向服务器发送并取回必须的数据,并在客户端采用JavaScript处理来自服务器的回应。由于在服务器和浏览器之间交换的数据大量减小(大约只有原来的5%)[来源请求],服务器回应更快了。同时,不少的处理工做能够在发出请求的客户端机器上完成,所以Web服务器的负荷也减小了。前端
相似于DHTML或LAMP,AJAX不是指一种单一的技术,而是有机地利用了一系列相关的技术。虽然其名称包含XML,但实际上数据格式能够由JSON代替,进一步减小数据量,造成所谓的AJAJ。而客户端与服务器也并不须要异步。一些基于AJAX的“派生/合成”式(derivative/composite)的技术也正在出现,如AFLAX。 —— 维基百科html5
JavaScript 编程的最大问题来自不一样的浏览器对各类技术和标准的支持。java
XmlHttpRequest 对象在不一样浏览器中不一样的建立方法,如下是跨浏览器的通用方法:node
// Provide the XMLHttpRequest class for IE 5.x-6.x:
// Other browsers (including IE 7.x-8.x) ignore this
// when XMLHttpRequest is predefined
var xmlHttp;
if (typeof XMLHttpRequest != "undefined") {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
var aVersions = ["Msxml2.XMLHttp.5.0", "Msxml2.XMLHttp.4.0",
"Msxml2.XMLHttp.3.0", "Msxml2.XMLHttp", "Microsoft.XMLHttp"];
for (var i = 0; i < aVersions.length; i++) {
try {
xmlHttp = new ActiveXObject(aVersions[i]);
break;
} catch (e) {}
}
}复制代码
详细信息请参考 - Can I use XMLHttpRequestjquery
Support | Features | |||||||
---|---|---|---|---|---|---|---|---|
All Browsers | Chrome & Firefox1 | Node | Concise Syntax | Promises | Native2 | Single Purpose3 | Formal Specification | |
XMLHttpRequest | ✓ | ✓ | ✓ | ✓ | ✓ | |||
Node HTTP | ✓ | ✓ | ✓ | ✓ | ||||
fetch() | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ||
Fetch polyfill | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ||
node-fetch | ✓ | ✓ | ✓ | ✓ | ✓ | |||
isomorphic-fetch | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
superagent | ✓ | ✓ | ✓ | ✓ | ✓ | |||
axios | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ||
request | ✓ | ✓ | ✓ | |||||
jQuery | ✓ | ✓ | ✓ | |||||
reqwest | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
1 Chrome & Firefox are listed separately because they support fetch()
: caniuse.com/fetch
2 Native: Meaning you can just use it - no need to include a library.
3 Single Purpose: Meaning this library or technology is ONLY used for AJAX / HTTP communication, nothing else.ios
详细信息请参考 - AJAX/HTTP Library Comparisongit
XMLHTTP 是一组API函数集,可被JavaScript、JScript、VBScript以及其它web浏览器内嵌的脚本语言调用,经过HTTP在浏览器和web服务器之间收发XML或其它数据。XMLHTTP最大的好处在于能够动态地更新网页,它无需从新从服务器读取整个网页,也不须要安装额外的插件。该技术被许多网站使用,以实现快速响应的动态网页应用。例如:Google的Gmail服务、Google Suggest动态查找界面以及Google Map地理信息服务。
XMLHTTP是AJAX网页开发技术的重要组成部分。除XML以外,XMLHTTP还能用于获取其它格式的数据,如JSON或者甚至纯文本。—— 维基百科
XMLHTTP最初是由微软公司发明的,在Internet Explorer 5.0中用做ActiveX对象,可经过JavaScript、VBScript或其它浏览器支持的脚本语言访问。Mozilla的开发人员后来在Mozilla 1.0中实现了一个兼容的版本。以后苹果电脑公司在Safari 1.2中开始支持XMLHTTP,而Opera从8.0版开始也宣布支持XMLHTTP。
大多数使用了XMLHTTP的设计良好的网页,会使用简单的JavaScript函数,将不一样浏览器之间调用XMLHTTP的差别性屏蔽,该函数会自动检测浏览器版本并隐藏不一样环境的差别。
在DOM 3(文档对象模型 Level 3)的读取和保存规范(Load and Save Specification)中也有相似的功能,它已经成为W3C推荐的方法。截止2011年,大多数浏览器已经支持。—— 维基百科
Microsoft ActiveX 控件是由软件提供商开发的可重用的软件组件。使用 ActiveX 控件,能够很快地在网址、台式应用程序、以及开发工具中加入特殊的功能。例如,StockTicker 控件能够用来在网页上即时地加入活动信息,动画控件可用来向网页中加入动画特性。
JavaScript 中 ActiveXObject 对象是启用并返回 Automation 对象的引用。
ActiveXObject 语法
newObj = new ActiveXObject(servername.typename[, location])复制代码
参数:
ActiveXObject 使用
// 在IE5.x和IE6下建立xmlHttp对象
// servername - MSXML2
// typename - XMLHTTP.3.0
var xmlHttp = new ActiveXObject('MSXML2.XMLHTTP.3.0');
xmlHttp.open("GET", "http://localhost/books.xml", false);
xmlHttp.send();复制代码
详细信息能够参考 - msdn - JavaScript 对象 - ActiveXObject 对象.aspx)
XMLHttpRequest 是一个API, 它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个经过 URL 来获取数据的简单方式,而且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。
XMLHttpRequest 是一个 JavaScript 对象,它最初由微软设计,随后被 Mozilla、Apple 和 Google采纳. 现在,该对象已经被 W3C组织标准化. 经过它,你能够很容易的取回一个URL上的资源数据. 尽管名字里有XML, 但 XMLHttpRequest 能够取回全部类型的数据资源,并不局限于XML。 并且除了HTTP ,它还支持file
和 ftp
协议。
XMLHttpRequest 语法
var req = new XMLHttpRequest();复制代码
XMLHttpRequest 使用
var xhr = new XMLHttpRequest(); // 建立xhr对象
xhr.open( method, url );
xhr.onreadystatechange = function () { ... };
xhr.setRequestHeader( ..., ... );
xhr.send( optionalEncodedData );复制代码
用于初始化一个 XMLHttpRequest 对象,必须在全部其它方法被调用前调用构造函数。使用示例以下:
var req = new XMLHttpRequest();复制代码
值 | 状态 | 描述 |
---|---|---|
0 | UNSENT (未打开) | 表示已建立 XHR 对象,open() 方法还未被调用 |
1 | OPENED (未发送) | open() 方法已被成功调用,send() 方法还未被调用 |
2 | HEADERS_RECEIVED (已获取响应头) | send() 方法已经被调用,响应头和响应状态已经返回 |
3 | LOADING (正在下载响应体) | 响应体下载中,responseText中已经获取了部分数据 |
4 | DONE (请求完成) | 整个请求过程已经完毕 |
值 | 响应数据类型 |
---|---|
"" | 字符串(默认值) |
"arraybuffer" | ArrayBuffer |
"blob" | Blob |
"document" | Document |
"json" | JSON |
"text" | 字符串 |
xhr.spec 规范中定义的 XMLHttpRequestResponseType 类型以下:
enum XMLHttpRequestResponseType {
"",
"arraybuffer",
"blob",
"document",
"json",
"text"
};复制代码
abort() - 若是请求已经被发送,则马上停止请求。
getAllResponseHeaders() - 返回全部响应头信息(响应头名和值),若是响应头尚未接收,则返回 null。注意:使用该方法获取的 response headers 与在开发者工具 Network 面板中看到的响应头不一致
getResponseHeader() - 返回指定响应头的值,若是响应头尚未被接收,或该响应头不存在,则返回 null。注意:使用该方法获取某些响应头时,浏览器会抛出异常,具体缘由以下:
W3C的 xhr 标准中作了限制,规定客户端没法获取 response 中的 Set-Cookie
、Set-Cookie2
这2个字段,不管是同域仍是跨域请求。
W3C 的 cors 标准对于跨域请求也作了限制,规定对于跨域请求,客户端容许获取的response header字段只限于 simple response header (常见的 simple response header 以下)
和 Access-Control-Expose-Headers。
open() - 初始化一个请求:
方法签名:
void open(
DOMString method,
DOMString url,
optional boolean async,
optional DOMString user,
optional DOMString password
);复制代码
参数:
备注:
SyntaxError
异常CONNECT
、TRACE
或 TRACK
将会抛出 SecurityError
异常overrideMimeType() - 重写由服务器返回的 MIME 类型。例如,能够用于强制把响应流当作 text/xml
来解析,即便服务器没有指明数据是这个类型。注意:这个方法必须在 send() 以前被调用。
send() - 发送请求。若是该请求是异步模式(默认),该方法会马上返回。相反,若是请求是同步模式,则直到请求的响应彻底接受之后,该方法才会返回。注意:全部相关的事件绑定必须在调用 send() 方法以前进行。
方法签名:
void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);复制代码
setRequestHeader() - 设置 HTTP 请求头信息。注意:在这以前,你必须确认已经调用了 open() 方法打开了一个 url
方法签名:
void setRequestHeader(
DOMString header,
DOMString value
);复制代码
参数:
sendAsBinary() - 发送二进制的 send() 方法的变种。
方法签名:
void sendAsBinary(
in DOMString body
);复制代码
参数:
Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari (WebKit) |
---|---|---|---|---|---|
Basic support (XHR1) | 1 | 1.0 | 5 (via ActiveXObject)7 (XMLHttpRequest) | (Yes) | 1.2 |
send(ArrayBuffer) | 9 | 9 | ? | 11.60 | ? |
send(Blob) | 7 | 3.6 | ? | 12 | ? |
send(FormData) | 6 | 4 | ? | 12 | ? |
response | 10 | 6 | 10 | 11.60 | ? |
responseType = 'arraybuffer' | 10 | 6 | 10 | 11.60 | ? |
responseType = 'blob' | 19 | 6 | 10 | 12 | ? |
responseType = 'document' | 18 | 11 | 未实现 | 未实现 | 未实现 |
responseType = 'json' | 未实现 | 10 | 未实现 | 12 | 未实现 |
Progress Events | 7 | 3.5 | 10 | 12 | ? |
withCredentials | 3 | 3.5 | 10 | 12 | 4 |
首先,建立一个 XMLHttpRequest 对象:
var xhr = new XMLHttpRequest();复制代码
而后,向服务器发出一个 HTTP 请求:
xhr.open('GET', 'example.php');
xhr.send();复制代码
接着,就等待远程主机作出回应。这时须要监控XMLHttpRequest对象的状态变化,指定回调函数。
xhr.onreadystatechange = function(){
if ( xhr.readyState == 4 && xhr.status == 200 ) {
alert( xhr.responseText );
} else {
alert( xhr.statusText );
}
};复制代码
上面的代码包含了老版本 XMLHttpRequest 对象的主要属性:
XMLHttpRequest Level 2 针对 XMLHttpRequest Level 1 的缺点,作了大幅改进。具体以下:
新版本 XMLHttpRequest 对象,增长了 timeout 属性,能够设置HTTP请求的时限。
xhr.timeout = 3000;复制代码
上面的语句,将最长等待时间设为3000毫秒。过了这个时限,就自动中止HTTP请求。与之配套的还有一个timeout事件,用来指定回调函数。
xhr.ontimeout = function(event){
console.log('请求超时');
}复制代码
AJAX 操做每每用来传递表单数据。为了方便表单处理,HTML 5新增了一个 FormData 对象,能够用于模拟表单。
构造函数 FormData()
用于建立一个新的 FormData 对象。
语法
var formData = new FormData(form)复制代码
<form>
表单元素。当使用 form 参数,建立的 FormData 对象会自动将 form 中的表单值也包含进去,文件内容会被编码首先,新建一个 FormData 对象:
var formData = new FormData();复制代码
而后,为它添加表单项:
formData.append('username', 'semlinker');
formData.append('id', 2005821040);复制代码
最后,直接传送这个FormData对象。这与提交网页表单的效果,彻底同样。
xhr.send(formData);复制代码
FormData 对象也能够用来获取网页表单的值。
var form = document.getElementById('myform'); // 获取页面上表单对象
var formData = new FormData(form);
formData.append('username', 'semlinker'); // 添加一个表单项
xhr.open('POST', form.action);
xhr.send(formData);复制代码
新版 XMLHttpRequest 对象,不只能够发送文本信息,还能够上传文件。
1.为了上传文件, 咱们得先选中一个文件. 一个 type 为 file 的 input 输入框
<input id="input" type="file">复制代码
2.而后用 FormData 对象包裹选中的文件
var input = document.getElementById("input"),
formData = new FormData();
formData.append("file",input.files[0]); // file名称与后台接收的名称一致复制代码
3.设置上传地址和请求方法
var url = "http://localhost:3000/upload",
method = "POST";复制代码
4.发送 FormData 对象
xhr.send(formData);复制代码
新版本的 XMLHttpRequest 对象,能够向不一样域名的服务器发出 HTTP 请求。这叫作 "跨域资源共享"(Cross-origin resource sharing,简称 CORS)。
使用"跨域资源共享"的前提,是浏览器必须支持这个功能,并且服务器端必须赞成这种"跨域"。若是可以知足上面的条件,则代码的写法与不跨域的请求彻底同样。
xhr.open('GET', 'http://other.server/and/path/to/script');复制代码
XMLHttpRequest Level 1 XMLHttpRequest 对象只能处理文本数据,新版则能够处理二进制数据。从服务器取回二进制数据,较新的方法是使用新增的 responseType 属性。若是服务器返回文本数据,这个属性的值是 "TEXT",这是默认值。较新的浏览器还支持其余值,也就是说,能够接收其余格式的数据。
你能够把 responseType 设为 blob,表示服务器传回的是二进制对象。
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = 'blob';
xhr.send();复制代码
接收数据的时候,用浏览器自带的 Blob 对象便可。
一个 Blob 对象表示一个不可变的, 原始数据的相似文件对象。Blob 表示的数据不必定是一个 JavaScript 原生格式。
File
接口基于Blob,继承 blob功能并将其扩展为支持用户系统上的文件。
var blob = new Blob([xhr.response], {type: 'image/png'});复制代码
更多示例请参考 发送和接收二进制数据 。
新版本的 XMLHttpRequest 对象,传送数据的时候,有一个 progress 事件,用来返回进度信息。
它分红上传和下载两种状况。下载的 progress 事件属于 XMLHttpRequest 对象,上传的 progress 事件属于XMLHttpRequest.upload 对象。
咱们先定义progress事件的回调函数:
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;复制代码
而后,在回调函数里面,使用这个事件的一些属性。
function updateProgress(event) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
}
}复制代码
上面的代码中,event.total 是须要传输的总字节,event.loaded 是已经传输的字节。若是event.lengthComputable 不为真,则 event.total 等于0。
各个浏览器 XMLHttpRequest Level 2 的兼容性 - Can I use/xhr2
XHR 能够传输基于文本和二进制数据。实际上,浏览器能够为各类本地数据类型提供自动编码和解码,这样可让应用程序将这些类型直接传递给XHR,以便正确编码,反之亦然,这些类型能够由浏览器自动解码:
XHR 下载图片示例:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://avatars2.githubusercontent.com/u/4220799?v=3');
xhr.responseType = 'blob'; // 1
xhr.onload = function() {
if (this.status == 200) {
var img = document.createElement('img');
img.src = window.URL.createObjectURL(this.response); // 2
img.onload = function() {
window.URL.revokeObjectURL(this.src); //3
};
document.body.appendChild(img);
}
};
xhr.send();复制代码
(1) 设置响应的数据类型为 blob
(2) 基于Blob建立一个惟一的对象URL,并做为图片的源地址 (URL.createObjectURL())
(3) 图片加载成功后释放对象的URL(URL.revokeObjectURL())
经过 XHR 上传数据对于全部数据类型来讲都是简单而有效的。实际上,惟一的区别是当咱们在XHR请求中调用 send() 时,咱们需传递不一样的数据对象。其他的由浏览器处理:
var xhr = new XMLHttpRequest();
xhr.open('POST','/upload');
xhr.onload = function() { ... };
xhr.send("text string"); // 1
var formData = new FormData(); // 2
formData.append('id', 123456);
formData.append('topic', 'performance');
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
xhr.send(formData); // 3
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
var uInt8Array = new Uint8Array([1, 2, 3]); // 4
xhr.send(uInt8Array.buffer); // 5复制代码
(1) 发送普通的文本到服务器
(2) 经过 FormData API 建立动态表单
(3) 发送 FormData 数据到服务器
(4) 建立 Unit8Array 数组 (Uint8Array 数组类型表示一个8位无符号整型数组,建立时内容被初始化为0)
(5) 发送二进制数据到服务器
XHR send() 方法签名:
void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);复制代码
除此以外,XHR 还支持大文件分块传输:
var blob = ...; // 1
const BYTES_PER_CHUNK = 1024 * 1024; // 2
const SIZE = blob.size;
var start = 0;
var end = BYTES_PER_CHUNK;
while(start < SIZE) { // 3
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
xhr.setRequestHeader('Content-Range', start+'-'+end+'/'+SIZE); // 4
xhr.send(blob.slice(start, end)); // 5
start = end;
end = start + BYTES_PER_CHUNK;
}复制代码
(1) 一个任意的数据块 (二进制或文本)
(2) 将数据库大小设置为 1MB
(3) 迭代提供的数据,增量为1MB
(4) 设置上传的数据范围 (Content-Range请求头)
(5) 经过 XHR 上传 1MB 数据块
XHR 对象提供了一系列 API,用于监听进度事件,表示请求的当前状态:
事件类型 | 描述 | 触发次数 |
---|---|---|
loadstart | 开始传输 | 1次 |
progress | 传输中 | 0次或屡次 |
error | 传输中出现错误 | 0次或1次 |
abort | 传输被用户取消 | 0次或1次 |
load | 传输成功 | 0次或1次 |
loadend | 传输完成 | 1次 |
每一个 XHR 传输都以 loadstart
事件开始,并以 loadend
事件结束,并在这两个事件期间触发一个或多个附加事件来指示传输的状态。所以,为了监控进度,应用程序能够在 XHR 对象上注册一组 JavaScript 事件侦听器:
var xhr = new XMLHttpRequest();
xhr.open('GET','/resource');
xhr.timeout = 5000; // 1
xhr.addEventListener('load', function() { ... }); // 2
xhr.addEventListener('error', function() { ... }); // 3
var onProgressHandler = function(event) {
if(event.lengthComputable) {
var progress = (event.loaded / event.total) * 100; // 4
...
}
}
xhr.upload.addEventListener('progress', onProgressHandler); // 5
xhr.addEventListener('progress', onProgressHandler); // 6
xhr.send();复制代码
(1) 设置请求超时时间为 5,000 ms (默认无超时时间)
(2) 注册成功回调
(3) 注册异常回调
(4) 计算已完成的进度
(5) 注册上传进度事件回调
(6) 注册下载进度事件回调
在某些状况下,应用程序可能须要或但愿逐步处理数据流:将数据上传到服务器,使其在客户机上可用,或者在从服务器下载数据时,进行流式处理。
var xhr = new XMLHttpRequest();
xhr.open('GET', '/stream');
xhr.seenBytes = 0;
xhr.onreadystatechange = function() { // 1
if(xhr.readyState > 2) {
var newData = xhr.responseText.substr(xhr.seenBytes); // 2
// process newData
xhr.seenBytes = xhr.responseText.length; // 3
}
};
xhr.send();复制代码
(1) 监听 onreadystatechange 事件
(2) 从部分响应中提取新数据
(3) 更新处理的字节偏移
这个例子能够在大多数现代浏览器中使用。可是,性能并很差,并且还有大量的注意事项和问题:
从服务器检索更新的最简单的策略之一是让客户端进行按期检查:客户端能够以周期性间隔(轮询服务器)启动后台XHR请求,以检查更新。若是新数据在服务器上可用,则在响应中返回,不然响应为空。
定时轮询的方式很简单,但若是定时间隔很短的话,也是很低效。所以设置合适的时间间隔显得相当重要:轮询间隔时间过长,会致使更新不及时,然而间隔时间太短的话,则会致使客户端与服务器没必要要的流程和高开销。接下来咱们来看一个简单的示例:
function checkUpdates(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() { ... }; // 1
xhr.send();
}
setInterval(function() { checkUpdates('/updates') }, 60000); // 2复制代码
(1) 处理服务端接收的数据
(2) 设置定时轮询时间为 60s
定时轮询会产生如下的问题:
每一个浏览器发起 HTTP 请求时都将携带额外的 500 - 800 字节的元数据 (请求头),如 user-agent、accept、Cache-Control 缓存控制头等。更糟糕的是,500 - 800 字节是理想的状况,若是携带 Cookies 信息,那么这个数值将会更大。总而言之,这些未压缩的 HTTP 元数据会引发很大开销。
平均每一个 HTTP 1.x 请求会增长 大约 800字节的请求和响应开销 (详细信息能够查看 - Measuring and Controlling Protocol Overhead) 。另外在客户端登陆后,咱们还将产生一个额外的身份验证 cookie 和 消息ID; 假设这又增长了50个字节。所以,不返回新消息的请求将产生 850字节开销!如今假设咱们有10,000个客户端,全部的轮询间隔时间都是60秒:
$$
(850 bytes 8 bits 10,000) / 60 seconds ≈ 1.13 Mbps
$$
每一个客户端在每一个请求上发送 850 字节的数据,这转换为每秒 167 个请求,服务器上的吞吐量大约为 1.13 Mbps!这不是一个固定的值,此外该计算值仍是在假设服务器没有向任何客户端传递任何新的消息的理想状况下计算而得的。
周期性轮询的挑战在于有可能进行许多没必要要的和空的检查。考虑到这一点,若是咱们对轮询工做流程进行了轻微的修改,而不是在没有更新可用的状况下返回一个空的响应,咱们能够保持链接空闲,直到更新可用吗?
(图片来源 - hpbn.co/xmlhttprequ…
经过保持长链接,直到更新可用,数据能够当即发送到客户端,一旦它在服务器上可用。所以,长时间轮询为消息延迟提供了最佳的状况,而且还消除了空检查,这减小了 XHR 请求的数量和轮询的整体开销。一旦更新被传递,长的轮询请求完成,而且客户端能够发出另外一个长轮询请求并等待下一个可用的消息:
function checkUpdates(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() { // 1
...
checkUpdates('/updates'); // 2
};
xhr.send();
}
checkUpdates('/updates'); // 3复制代码
(1) 处理接收到的数据并启动下一轮检测更新
(2) 启动下一轮检测更新
(3) 发起首次更新请求
那么长时间轮询老是比按期轮询更好的选择?除非消息到达率已知且不变,不然长轮询将始终提供更短的消息延迟。
另外一方面,开销讨论须要更细微的观点。首先,请注意,每一个传递的消息仍然引发相同的 HTTP 开销;每一个新消息都是独立的 HTTP 请求。可是,若是消息到达率高,那么长时间轮询会比按期轮询发出更多的XHR请求!
长轮询经过最小化消息延迟来动态地适应消息到达速率,这是您可能想要的或可能不须要的行为。若是对消息延迟要求不高的话,则定时轮询多是更有效的传输方式 - 例如,若是消息更新速率较高,则定时轮询提供简单的 "消息聚合" 机制 (即合并必定时间内的消息),这能够减小请求数量并提升移动设备的电池寿命。
Mock.js 是一款模拟数据生成器,旨在帮助前端攻城师独立于后端进行开发,帮助编写单元测试。提供了如下模拟功能:
详细信息,请查看 - Mock.js 文档
Zone 是下一个 ECMAScript 规范的建议之一。Angular 团队实现了 JavaScript 版本的 zone.js ,它是用于拦截和跟踪异步工做的机制。
Zone 是一个全局的对象,用来配置有关如何拦截和跟踪异步回调的规则。Zone 有如下能力:
zone.js 内部使用 Monkey Patch 方式,拦截 XMLHttpRequest.prototype 对象中的 open、send、abort 等方法。
// zone.js 源码片断
var openNative = patchMethod(window.XMLHttpRequest.prototype, 'open', function () {
return function (self, args) {
self[XHR_SYNC] = args[2] == false;
return openNative.apply(self, args);
};
});复制代码
Oboe.js 经过将 HTTP 请求-应答模型封装在一个渐进流式接口中,帮助网页应用快速应答。它将 streaming 和downloading 间的转换与SAX和DOM间JSON的解析整合在一块儿。它是个很是小的库,不依赖于其余程序库。它能够在 ajax 请求结束前就开始解析 json 变得十分容易,从而提升应用的应答速度。另外,它支持 Node.js 框架,还能够读入除了 http 外的其余流。
有兴趣的读者,推荐看一下官网的可交互的演示示例 - Why Oboe.js
(备注:该库就是文中 - 使用XHR流式传输数据章节的实际应用,不信往下看)
// oboe-browser.js 源码片断
function handleProgress() {
var textSoFar = xhr.responseText,
newText = textSoFar.substr(numberOfCharsAlreadyGivenToCallback);
if( newText ) {
emitStreamData( newText );
}
numberOfCharsAlreadyGivenToCallback = len(textSoFar);
}复制代码
fetch 函数是一个基于 Promise 的机制,用于在浏览器中以编程方式发送 Web 请求。该项目是实现标准 Fetch 规范的一个子集的 polyfill ,足以做为传统 Web 应用程序中 XMLHttpRequest 的代替品。
详细信息,请参考 - Github - fetch
Fetch API 兼容性,请参考 - Can I use Fetch
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}复制代码
代码片断来源 - ArrayBuffer与字符串的互相转换
function str2ab(str) {
var buf = new ArrayBuffer(str.length * 2); // 每一个字符占用2个字节
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}复制代码
代码片断来源 - ArrayBuffer与字符串的互相转换
// Provide the XMLHttpRequest class for IE 5.x-6.x:
// Other browsers (including IE 7.x-8.x) ignore this
// when XMLHttpRequest is predefined
var xmlHttp;
if (typeof XMLHttpRequest != "undefined") {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
var aVersions = ["Msxml2.XMLHttp.5.0", "Msxml2.XMLHttp.4.0",
"Msxml2.XMLHttp.3.0", "Msxml2.XMLHttp", "Microsoft.XMLHttp"];
for (var i = 0; i < aVersions.length; i++) {
try {
xmlHttp = new ActiveXObject(aVersions[i]);
break;
} catch (e) {}
}
}复制代码
var xmlHttp;
if (typeof XMLHttpRequest != "undefined") {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {}
}复制代码
if (!XMLHttpRequest.prototype.sendAsBinary) {
XMLHttpRequest.prototype.sendAsBinary = function (sData) {
var nBytes = sData.length, ui8Data = new Uint8Array(nBytes);
for (var nIdx = 0; nIdx < nBytes; nIdx++) {
ui8Data[nIdx] = sData.charCodeAt(nIdx) & 0xff;
}
this.send(ui8Data);
};
}复制代码
function readBody(xhr) {
var data;
if (!xhr.responseType || xhr.responseType === "text") {
data = xhr.responseText;
} else if (xhr.responseType === "document") {
data = xhr.responseXML;
} else {
data = xhr.response;
}
return data;
}复制代码
应用示例:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
console.log(readBody(xhr));
}
}
xhr.open('GET', 'https://www.baidu.com', true);
xhr.send(null);复制代码
export function getResponseURL(xhr: any): string {
if ('responseURL' in xhr) {
return xhr.responseURL;
}
if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
return xhr.getResponseHeader('X-Request-URL');
}
return;
}复制代码
代码片断来源 - Github - @angular/http - http_utils.ts
export const isSuccess = (status: number): boolean => (status >= 200 && status < 300);复制代码
代码片断来源 - Github - @angular/http - http_utils.ts
function paramParser(rawParams: string = ''): Map<string, string[]> {
const map = new Map<string, string[]>();
if (rawParams.length > 0) {
const params: string[] = rawParams.split('&');
params.forEach((param: string) => {
const eqIdx = param.indexOf('=');
const [key, val]: string[] =
eqIdx == -1 ? [param, ''] : [param.slice(0, eqIdx), param.slice(eqIdx + 1)];
const list = map.get(key) || [];
list.push(val);
map.set(key, list);
});
}
return map;
}复制代码
代码片断来源 - Github - @angular/http - url_search_params.ts
ts 转换为 js 的代码以下:
function paramParser(rawParams) {
if (rawParams === void 0) { rawParams = ''; }
var map = new Map();
if (rawParams.length > 0) {
var params = rawParams.split('&');
params.forEach(function (param) {
var eqIdx = param.indexOf('=');
var _a = eqIdx == -1 ? [param, ''] :
[param.slice(0, eqIdx), param.slice(eqIdx + 1)], key = _a[0],
val = _a[1];
var list = map.get(key) || [];
list.push(val);
map.set(key, list);
});
}
return map;
}复制代码
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://avatars2.githubusercontent.com/u/4220799?v=3');
xhr.responseType = 'blob';
xhr.onload = function() {
if (this.status == 200) {
var img = document.createElement('img');
img.src = window.URL.createObjectURL(this.response);
img.onload = function() {
window.URL.revokeObjectURL(this.src);
};
document.body.appendChild(img);
}
};
xhr.send();复制代码
var xhr = new XMLHttpRequest();
xhr.open('POST','/upload');
xhr.onload = function() { ... };
xhr.send("text string");复制代码
var formData = new FormData();
formData.append('id', 123456);
formData.append('topic', 'performance');
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
xhr.send(formData);复制代码
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
var uInt8Array = new Uint8Array([1, 2, 3]);
xhr.send(uInt8Array.buffer);复制代码
progress 元素
<progress id="uploadprogress" min="0" max="100" value="0">0</progress>复制代码
定义 progress 事件的回调函数
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var complete = (event.loaded / event.total * 100 | 0);
var progress = document.getElementById('uploadprogress');
progress.value = progress.innerHTML = complete;
}
};复制代码
注意,progress事件不是定义在xhr,而是定义在xhr.upload,由于这里须要区分下载和上传,下载也有一个progress事件。
XMLHttpRequest 返回 status 时,会执行如下步骤:
另外当访问本地文件资源或在 Android 4.1 stock browser 中从应用缓存中获取文件时,XMLHttpRequest 的 status 值也会为0。
示例一:
var xmlhttp;
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","http://www.w3schools.com/XML/cd_catalog.xml", true);
xmlhttp.onreadystatechange=function() {
if(xmlhttp.readyState == 4) console.log("status " + xmlhttp.status);
};
xmlhttp.addEventListener('error', function (error) {
console.dir(error);
});
xmlhttp.send();复制代码
以上代码运行后,将会在控制台输出:
status 0
ProgressEvent # error 对象复制代码
client . send([body = null])
Initiates the request. The optional argument provides the request body. The argument is ignored if request method is
GET
orHEAD
. —— xhr.spec
经过 XMLHttpRequest 规范,咱们知道当请求方法是 GET 或 HEAD 时,send()
方法的 body 参数值将会被忽略。那么对于咱们经常使用的 GET 请求,咱们要怎么传递参数呢?解决参数传递可使用如下两种方式:
URL 传参
var url = "bla.php";
var params = "somevariable=somevalue&anothervariable=anothervalue";
var http = new XMLHttpRequest();
http.open("GET", url+"?"+params, true);
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(null); // 请求方法是GET或HEAD时,设置请求体为空复制代码
在平常开发中,咱们最经常使用的方式是传递参数对象,所以咱们能够封装一个 formatParams()
来实现参数格式,具体示例以下:
formatParams() 函数:
function formatParams( params ){
return "?" + Object
.keys(params)
.map(function(key){
return key+"="+params[key]
})
.join("&")
}复制代码
应用示例:
var endpoint = "https://api.example.com/endpoint";
var params = {
a: 1,
b: 2,
c: 3
};
var url = endpoint + formatParams(params); // 实际应用中须要判断endpoint是否已经包含查询参数
// => "https://api.example.com/endpoint?a=1&b=2&c=3";复制代码
一些经常使用的 AJAX 库,如 jQuery、zepto 等,内部已经封装了参数序列化的方法 (如:jquery.param),咱们直接调用顶层的 API 方法便可。
(备注:以上示例来源 - stackoverflow - How do I pass along variables with XMLHttpRequest)
请求头传参 - (身份认证)
var xhr = new XMLHttpRequest();
xhr.open("POST", '/server', true);
xhr.setRequestHeader("x-access-token", "87a476494db6ec53d0a206589611aa3f");
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
// handle data
}
};
xhr.send("foo=bar&lorem=ipsum");复制代码
详细的身份认证信息,请参考 - JSON Web Tokens
send() 方法签名:
void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);复制代码
POST请求示例
发送 POST 请求一般须要如下步骤:
Content-Type
请求头var xhr = new XMLHttpRequest();
xhr.open("POST", '/server', true);
//Send the proper header information along with the request
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
// handle data
}
}
xhr.send("foo=bar&lorem=ipsum");
// xhr.send('string');
// xhr.send(new Blob());
// xhr.send(new Int8Array());
// xhr.send({ form: 'data' });
// xhr.send(document);复制代码
简单请求
一些不会触发 CORS preflight 的请求被称为 "简单请求",虽然 Fetch (定义 CORS的) 不使用这个术语。知足下述条件的就是 "简单请求":
Content-Type
值有:
预请求
不一样于上面讨论的简单请求,"预请求" 要求必须先发送一个 OPTIONS
方法请求给目的站点,来查明这个跨站请求对于目的站点是否是安全的可接受的。这样作,是由于跨站请求可能会对目的站点的数据产生影响。 当请求具有如下条件,就会被当成预请求处理:
详细的信息,请参考 - MDN - HTTP 访问控制 (CORS)
在如下状况下,XMLHttpRequest 对象不会被垃圾回收:
OPENED
且已设置 send()
的标识符若是 XMLHttpRequest 对象在链接尚存打开时被垃圾回收机制回收了,用户代理必须终止请求。
详细的信息,请参考 - 99%的人都理解错了HTTP中GET与POST的区别
详细的信息,请参考 - 知乎 - 怎样防止重复发送 Ajax 请求
众所周知,大部分的搜索引擎爬虫都不会执行 JS,也就是说,若是页面内容由 Ajax 返回的话,搜索引擎是爬取不到部份内容的,也就无从作 SEO (搜索引擎优化)了。国外的 prerender.io 网站提供了一套比较成熟的方案,可是须要付费的。接下来咱们来看一下,怎么 PhantomJS 为咱们的站点作 SEO。
详细的信息,请参考 - 用PhantomJS来给AJAX站点作SEO优化