之前在项目中看到过一段上传文件的代码,其中有这样一句:
const form = new FormData();
当时不知道这个FormData()
是个什么东西,好像也没有这样的构造函数,但是用的时候都是复制粘贴的,代码也能正常运行,所以也就没有多想。
今天无意中查了一下XMLHttpRequest,原来这玩意还有Level 1标准和Level 2标准,我知道的那些都是Level 1,而FormData
是Level 2里面的新增特性,下面就来系统总结一下。
什么是 XMLHttpRequest 对象
以下内容引用自W3School:
XMLHttpRequest 对象用于在后台与服务器交换数据。
- 在不重新加载页面的情况下更新网页
- 在页面已加载后从服务器请求数据
- 在页面已加载后从服务器接收数据
- 在后台向服务器发送数据
如何创建 XMLHttpRequest 对象
这里回顾一下Level 1的用法。
首先,新建一个XMLHttpRequest的实例。
var xhr= new XMLHttpRequest();
然后,向远程主机发出一个HTTP请求。这里的第三个参数Async规定请求是否异步处理,true或者不传都表示脚本会在 send() 方法之后继续执行,而不等待来自服务器的响应。也就是说,send()这句是异步的。
xhr.open("GET", "https://www.xiaomizhou.com", true);
xhr.send();
接着,就等待远程主机做出回应。这时需要监控XMLHttpRequest对象的状态变化,指定回调函数。
xhr.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
console.log(request.responseText)
}
}
XMLHttpRequest对象的主要属性
xhr.readyState:XMLHttpRequest对象的状态,一共有5种状态
0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用了
xhr.status:服务器返回的状态码,等同于HTTP的状态
200:请求成功
400: 内部服务器错误
xhr.responseText:服务器返回的文本数据
xhr.responseXML:服务器返回的XML格式的数据
xhr.statusText:服务器返回的状态文本
request.onload = function(e){} 请求成功
request.process = function(e){} 请求正在加载
request.onerror = function(e){} 请求失败
老版本的缺点
老版本的XMLHttpRequest对象有以下几个缺点:
1. 只支持文本数据的传送,无法用来读取和上传二进制文件。
2. 传送和接收数据时,没有进度信息,只能提示有没有完成。
3. 受到"同域限制"(Same Origin Policy),只能向同一域名的服务器请求数据。
新版本的功能
新版本的XMLHttpRequest对象,针对老版本的缺点,做出了大幅改进。
1. 设置HTTP请求的时限
新版本的XMLHttpRequest对象,增加了timeout属性。下面的语句,将最长等待时间设为3000毫秒。过了这个时限,就自动停止HTTP请求。
xhr.timeout = 3000;
与之配套的还有一个timeout事件,用来指定回调函数。
xhr.ontimeout = function(event){
alert('请求超时!');
}
2. FormData对象
ajax操作往往用来传递表单数据。为了方便表单处理,HTML 5新增了一个FormData对象,可以模拟表单。
首先,新建一个FormData对象。
var formData = new FormData();
然后,为它添加表单项。
formData.append('username', '张三');
formData.append('id', 123456);
最后,直接传送这个FormData对象。这与提交网页表单的效果,完全一样。
xhr.send(formData);
FormData对象也可以用来获取网页表单的值。
var form = document.getElementById('myform');
var formData = new FormData(form);
formData.append('secret', '123456'); // 添加一个表单项
xhr.open('POST', form.action);
xhr.send(formData);
FormData对象最大的作用是上传文件。假定files是一个"选择文件"的表单元素(input[type=“file”]),我们将它装入FormData对象。
var formData = new FormData();
for (var i = 0; i < files.length;i++) {
formData.append('files[]', files[i]);
}
然后,发送这个FormData对象。
xhr.send(formData);
3. 跨域资源共享(CORS)
新版本的XMLHttpRequest对象,可以向不同域名的服务器发出HTTP请求。
使用"跨域资源共享"的前提,是浏览器必须支持这个功能,而且服务器端必须同意这种"跨域"。如果能够满足上面的条件,则代码的写法与不跨域的请求完全一样。
xhr.open('GET', 'http://other.server/and/path/to/script');
4. 进度信息
新版本的XMLHttpRequest对象,传送数据的时候,有一个progress事件,用来返回进度信息。
它分成上传和下载两种情况。下载的progress事件属于XMLHttpRequest对象,上传的progress事件属于XMLHttpRequest.upload对象。
首先,定义事件的回调函数。
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。
与progress事件相关的。还有其他5个事件,可以分别指定回调函数。
load:传输成功完成。
abort:传输被用户取消。
error:传输中出现错误。
loadstart:传输开始。
loadEnd:传输结束,但不知道成功还是失败。
Promise封装ajax请求
下面代码中,getJSON
是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise
对象。
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
参考:
XMLHttpRequest 详解
XMLHttpRequest Level 2 使用指南 - 阮一峰
Promise对象 - 阮一峰