XMLHttpRequest对象简介
纲要
XMLHttpRequest
是一个内建的浏览器对象,Ajax技术的核心就是XMLHttpRequest
对象。
jQuery中的 $.ajax()
、$.get()
、$.post()
的底层实现,就是 XMLHttpRequest
。
历史
-
XMLHttpRequest对象最初是 WHATWG(超文本应用程序技术工作组) 的一部分。
-
2006年移至W3C。
-
2008年2月,对XMLHttpRequest进行了扩展(如进度事件和跨域请求),也就是所谓的XMLHttpRequest Level 2,直至2011年底。
-
2012年底,它移回了 WHATWG。
小结
-
XMLHttpRequest 简称 XHR 对象,是一个浏览器内置对象,目前,所有的浏览器均支持这个对象。
-
它的作用是可以实现Ajax请求,Ajax技术的核心就是
XMLHttpRequest
对象。
实现Ajax的GET请求
步骤
-
创建xhr对象。
-
注册 xhr.onreadystatechange 事件。在 readystatechange 函数中,接收响应结果。
-
调用open方法,初始化一个请求,此方法用于配置请求方式和url。
-
调用send方法,发送请求。
基础代码
// 1. 创建xhr对象 var xhr = new XMLHttpRequest(); // 2. 注册 xhr.onreadystatechange 事件,当Ajax请求**成功**后,会触发onload函数。在 readystatechange 函数中,接收响应结果。 xhr.onreadystatechange = function () { // 使用 xhr.response 接收响应结果 var res = xhr.responseText; } // 3. 调用open方法,设置请求方式及请求的url地址 // xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks'); xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888'); // 4. 最后,调用send方法,发送这次ajax请求 xhr.send();
请求参数
重要:如果传递请求参数,请求参数要以查询字符串形式拼接到url后面。
形如:url?id=1&appkey=13200008888
GET和DELETE请求,需要以这种方式来传递参数。
其他说明
API | 兼容性 |
---|---|
XMLHttpRequest | IE7+ 支持 |
open | 所有浏览器均支持 |
send | 所有浏览器均支持 |
onreadystatechange | 所有浏览器均支持 |
responseText | 所有浏览器均支持 |
了解 XMLHttpRequest
对象所有API的兼容性,点击这里。
实现Ajax的POST请求
步骤
(比GET请求多了一行代码)
-
创建xhr对象。
-
注册 xhr.onreadystatechange 事件。在 readystatechange 函数中,接收响应结果。
-
调用open方法,初始化一个请求,此方法用于配置请求方式和url。
-
调用setRequestHeader方法,设置请求头。
-
调用send方法,发送请求。
基础代码
// 1. 创建xhr对象 var xhr = new XMLHttpRequest(); // 2. 注册 xhr.onreadystatechange 事件,当Ajax请求**成功**后,会触发onload函数。在 readystatechange 函数中,接收响应结果。 xhr.onreadystatechange = function () { // 使用 xhr.response 接收响应结果 var res = xhr.responseText; } // 3. 调用open方法,设置请求方式及请求的url地址 xhr.open('POST', 'http://www.itcbc.com:3006/api/addbook'); // 4. 调用setRequestHeader,设置请求头,目的是告知服务器以何种方式解析请求体 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // 5. 最后,调用send方法,发送这次ajax请求 xhr.send('bookname=西游记&author=唐僧&publisher=大唐出版社&appkey=13200008888');
请求体
重申一下,POST请求的参数,称之为请求体。
和GET请求一样,请求体也要写成查询字符串格式,不同之处是,请求体做为send方法的参数发送。
其他说明
API | 兼容性 |
---|---|
setRequestHeader | 所有浏览器均支持 |
setRequestHeader
方法用于设置请求头,格式如下:
xhr.setRequestHeader('名称', '值');
-
设置的请求头可以在 network 面板 > Headers > Request Headers 中查看。
-
大部分请求头由浏览器管理,不允许我们修改。所以我们无需关心。
-
一旦设置了请求头,就无法撤销了。
Content-Type
-
只有POST、PUT请求,才需要设置
Content-Type
请求头 -
这里设置的
Content-Type
,作用是告知服务器,浏览器提交的数据是何种类型的。-
值为:application/x-www-form-urlencoded(需要自己指定) ,表示客户端提交的是查询字符串。
-
值为:application/json(需要自己指定) ,表示客户端提交的是 JSON 字符串。
-
值为:multipart/form-data(xhr对象会自动设置),表示客户端提交的是 FormData 对象。
-
对请求参数进行编码
什么是URL编码?
-
把中文和部分特殊符号转成 URL的标准格式 ,这就是url编码
-
如,把“中文” 进行url编码后得到 “%E4%B8%AD%E6%96%87”
为什么要对请求参数进行编码
-
RFC文档规定,只有字母和数字[0-9a-zA-Z]、一些特殊符号“$-_.+!*’(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL。
-
对于Unicode字符,RFC文档建议使用utf-8对其进行编码得到相应的字节,然后对每个字节执行百分号编码,也就是所谓的 URL编码。
编码能够解决的问题
-
乱码问题(浏览器使用utf-8、服务器使用其他编码,传输数据的时候,就会乱码)
-
提交的内容有特殊符号问题,比如添加一本书,书名是 “红&黑”
如何进行URL编码
JS提供了内置编码解码函数
-
编码:encodeURIComponent('西游记')
-
解码:decodeURIComponent("%E8%A5%BF%E6%B8%B8%E8%AE%B0");
具体查看手册:JavaScript 全局参考手册
小结
无论是GET请求还是POST请求,向服务器传递参数的时候,必须要对参数进行编码。这里的参数包括参数名和值。
比如,参数为 shu ming=红&黑
-
参数名中有空格,需要编码;
-
值有中文,且有歧义,需要编码;
readyState属性
Ajax从创建xhr对象开始,一直到完全接收服务器返回的结果为止;我们可以把整个请求响应过程划分为5个阶段。并且可以使用 xhr.readyState 属性检测当前请求执行到哪个阶段了。
readyState属性值为一个数字,不同的数字表示Ajax的不同状态。
-
如果状态值为0(xhr.readyState === 0),初始状态,表示xhr对象一定创建了。
-
如果状态值为1(xhr.readyState === 1),表示open一定调用了
-
如果状态值为2(xhr.readyState === 2),表示send一定调用了,并且已经接收到响应头。
-
如果状态值为3(xhr.readyState === 3),表示正在接收服务器返回的数据(可能已接收完毕,也可能正在接收中,取决于数据量的大小)
-
如果状态值为
4
(xhr.readyState === 4
),表示Ajax请求~响应过程完成
值为0、1、2、3的时候,基本上不用关心。主要关心值为4的时候,因为这个时候表示Ajax请求~响应过程完成了,如果成功的结束了,则可以完全接收服务器响应的结果。
下面在创建xhr对象后和onreadystatechange事件内部分别输出 xhr.readyState 可以分别得到 0 、1、2、3、4。
// 1. 创建xhr对象 var xhr = new XMLHttpRequest(); console.log(xhr.readyState); // ===> 0 // 2. 注册 xhr.onreadystatechange 事件,当Ajax请求**成功**后,会触发onload函数。在 readystatechange 函数中,接收响应结果。 xhr.onreadystatechange = function () { console.log(xhr.readyState); // ===> 1/2/3/4 // 使用 xhr.response 接收响应结果 var res = xhr.responseText; } // 3. 调用open方法,设置请求方式及请求的url地址 // xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks'); xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888'); // 4. 最后,调用send方法,发送这次ajax请求 xhr.send();
onreadystatechange事件
介绍
onreadystatechange 翻译过来是 当Ajax的请求状态改变的时候。
所以,它是配合上述的 readyState 使用的事件。
事件具体的触发时机如下:
-
readyState属性值改变的时候
-
0 --> 1
-
1 --> 2
-
2 --> 3
-
3 --> 4
-
-
接收到的数据量改变的时候,此时 readyState 的值保持为3,但也会触发 onreadystatechange 事件(发生在分块接收大量数据的时候)
所以,绑定的onreadystatechange事件,可能会触发多次。
完整代码
前文提到,onload有浏览器兼容问题,如果你的项目需要支持低版本的浏览器,那么可以使用 onreadystatechange事件代替onload事件。
由于onreadystatechange事件可能会触发多次,所以需要在事件中加入判断,已保证准确的接收到响应结果。
// 1. 创建xhr对象 var xhr = new XMLHttpRequest(); // IE6 创建对象 var xhr = new ActiveXObject('Microsoft.XMLHTTP'); // 2. 使用onreadystatechange代替onload xhr.onreadystatechange = function () { // 判断Ajax请求是否完成 if(xhr.readyState === 4) { // 还要根据响应状态码判断,请求是否成功 if (xhr.status === 200) { console.log(xhr.responseText); } else { console.log('请求失败') } } } // 3. 调用open方法,设置请求方式及请求的url地址(如有参数,拼接到url后面) xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888'); // 4. 最后,调用send方法,发送这次ajax请求 xhr.send();
设置HTTP请求时限
-
timeout -- Level 2 新增,IE8+支持,用于设置请求的超时时间,单位是毫秒
-
ontimeout -- Level 2 新增,IE10+支持,如果请求超时了,会触发 ontimeout 事件
var xhr = new XMLHttpRequest() xhr.onreadystatechange = function () { if (this.readyState === 4 && this.status === 200) { console.log(this.responseText); } } xhr.timeout = 30; // 单位是毫秒 // 当请求超时之后,会触发下面的函数 xhr.ontimeout = function () { alert('请求超时,请刷新重试'); } xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888'); xhr.send();
xhr2新增事件
-
ontimeout -- 请求超时之后,触发的事件
-
onload -- 请求成功后触发的事件(因为已经表示成功后触发了,所以事件内部就不要加if判断了)
-
onerror -- 请求失败后触发的事件,比如网络不通造成的请求发送失败
-
onloadstart -- 请求开始的时候,触发的事件
-
onloadend -- 请求完成后触发的事件,这里的完成包括成功和失败
-
onprogress -- 客户端下载(接收)数据的时候,触发的事件
FormData对象
介绍
Form是表单,Data是数据。猜测,它和表单数据有关系。
FormData是h5出现之后,新增的一个对象。用于管理表单数据。IE10+支持。
创建的FormData对象,可直接通过 xhr.send(FormData对象)
提交给服务器的接口。
基本语法
var fd = new FormData([form]); // 参数是表单的DOM对象,可选
FormData的API:(除了append方法IE10支持外,其他方法IE均不支持)
-
append('key', 'value');
-- 向对象中追加数据 -
set('key', 'value');
-- 修改对象中的数据 -
delete('key'); -- 从对象中删除数据
-
get('key') -- 获取对象中的数据
-
getAll('key') -- 获取指定key的全部数据
-
forEach() -- 遍历对象中的数据
-
....
<form action=""> <input type="text" name="username"><br /> <input type="password" name="pwd"><br /> <input type="radio" name="sex" value="nan"> <input type="radio" name="sex" value="nv"><br /> <button>提交</button> </form> <script> document.querySelector('form').onsubmit = function (e) { e.preventDefault(); // 使用FormData收集表单数据 var fd = new FormData(this); // 传入表单的DOM对象 // append向fd对象中追加数据 fd.append('age', 20); // 追加一个age fd.append('username', 'lisi'); // 追加一个username // set修改fd中的数据 fd.set('sex', 'yao'); // 修改sex // get用于获取一个值 console.log( fd.get('username') ); // 获取到一个username的值 console.log( fd.getAll('username') ); // 获取到全部username的值 // console.log(fd); // 输出fd没用,看不到数据 // 只能通过forEach来检查对象中有哪些数据 fd.forEach(function (val, key) { // console.log(key, val); }); } </script>
提交FormData
FormData本就是配合XHR对象一起使用的
前面我们收集到了很多值,现在,我们可以通过Ajax,将这些值提交给接口。
-
只能通过POST方式提交FormData对象
-
不能设置Content-Type请求头,因为当提交FormData的时候,浏览器会自动设置这个请求头。
<form action=""> 姓名:<input type="text" name="username"><br> 年龄:<input type="text" name="age"><br> 身高:<input type="text" name="height"><br> <button>提交</button> </form> <script> document.querySelector('form').onsubmit = function (e) { e.preventDefault(); var fd = new FormData(this); // ajax提交数据到接口 var xhr = new XMLHttpRequest(); xhr.onload = function () { console.log(this.responseText); } xhr.open('POST', 'http://www.itcbc.com:3006/api/formdata'); // xhr.setRequestHeader(); // 使用FormData,不要设置请求头;写了请求头,反而会报错 xhr.send(fd); } </script>
如果提交FormData数据给接口,需要接口的支持。比如添加图书、添加评论接口都不支持提交FormData数据。
注意事项(必看)
-
使用FormData,要求表单各项必须有name属性,因为FormData也是根据表单各项的name属性获取值的
-
不能收集禁用状态的值。
-
实例化 FormData对象,传入表单的DOM对象,可以快速收集到表单各项值。
-
可以收集文件信息。可以完成文件上传。
-
如果要检查FormData中有哪些值,需要使用forEach遍历。
-
如果需要动态添加或修改FormData中的值,可以调用 FormData的append或set方法。
上传文件并显示进度条
实现过程:
-
先完成提交文件,即使用FormData获取文件域的内容,然后Ajax提交FormData对象给接口。
-
再能够上传文件的基础之上,注册xhr.upload.onprogress事件,监听上传进度变化,制作进度条
参考代码:
<form> <input type="file" name="avatar"> <button>上传</button> </form> <!-- 设置一个进度条,这里使用h5新标签,开始隐藏 --> <progress max="0" value="0" style="display: none"></progress> <script> document.querySelector('form').onsubmit = function (e) { e.preventDefault(); // 使用FormData收集输入框的值(这里是选择的图片) var fd = new FormData(this); // ajax提交fd对象 // 接口文档,查看ppt即可 var xhr = new XMLHttpRequest(); /*************************************************/ /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ // 找到 进度条标签 var progress = document.querySelector('progress'); // 注册上传监听事件 xhr.upload.onprogress = function (e) { progress.style.display = 'block'; progress.max = e.total; // 文件总大小,单位字节 progress.value = e.loaded; // 已经上传了多少字节 } /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/ /************************************************/ xhr.onload = function () { console.log(this.responseText); } xhr.open('POST', 'http://www.itcbc.com:3006/api/formdata'); xhr.send(fd); } </script>
注意,上传进度使用xhr.upload.onprogress事件;下载进度使用xhr.onprogress事件;