AJAX
异步的JavaScript与XML技术( Asynchronous JavaScript and XML )
Ajax 核心使用 `XMLHttpRequest` (XHR)对象,首先由微软引入的一个特性;Ajax 不需要任何浏览器插件,能在不更新整个页面的前提下维护数据(可以向服务器请求额外的数据无需重载页面),但需要用户允许JavaScript在浏览器上执行。
XHR 对象用法
1 var xhr = new XMLRequestHttp() // 通过XMLHttpRequest 构造函数来创建
open 方法
xhr.open(method, url, async, user, password);
method:要发送的请求类型 GET、POST、PUT、DELETE 。(必选)
url:请求的URL (必选)
axync :布尔值,是否异步发送请求,默认true(true 时,已完成事务的通知可供事件监听使用,如果 xhr.multipart为true,则此参数必须为true;false 时,send()方法直到收到答复前不会返回)
user:用户名用于认证用途 默认 null
password:用户名用于认证用途 默认 null
调用open方法不会真正发送请求,只是准备发送请求,并且URL有同源策略的限制(须和页面的主域、端口、协议一致,只要一处不符合要求将报错,数据将被拦截,可通过前后端配置,或使用代理来解决)。
setRequestHeader()
如需设置 Accpet 头部信息,可通过setRequestHeader() 方法来设置
Accpet 头部信息:告知客户端可以处理的内容类型,用 MIME类型 表示;使用 Content-Type 服务端使用 `Content-Type` 通知客户端它的选择
媒体类型( MIME类型 ) :一种标准,用来表示文档、文件或字节流的性质和格式。 完整类型列表
Content-Type :实体头部用于指示资源的 MIME 类型,告诉客户端实际返回的内容类型;浏览器会在某些情况下进行MIME查找,并不一定遵循此标题的值; 为了防止这种行为,可以将标题 X-Content-Type-Options 设置为 nosniff。
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
send 方法
xhr.send(data);
data:作为请求主体发送的数据,如果不需要通过请求主体发送数据,则必须传 null
调用 send()发送请求,在收到响应后,响应的数据会自动填充XHR对象的属性
responseText :从服务端返回的文本
1 xhr.onload = function () {
2 if (xhr.readyState === xhr.DONE) {
3 if (xhr.status === 200) {5 console.log(xhr.responseText);
6 }
7 }
8 };
responseXML
如果响应的 Content-Type 为 text/html 或 application/xml,将保存包含响应数据的 XML DOM 文档,对于其它类型的数据则为 null, 也可通过overrideMimeType() 强制 XHR 对象解析为 XML
1 // overrideMimeType() 用来强制解析 response 为 XML
2 xhr.overrideMimeType('text/xml');
3
4 xhr.onload = function () {
5 if (xhr.readyState === xhr.DONE) {
6 if (xhr.status === 200) { 8 console.log(xhr.responseXML);
9 }
10 }
11 };
status
返回响应的HTTP状态码,请求完成前值为0,如果XHR 对象出错 值也是0, 200 表示请求成功,304表示请求的资源并没有修改,可直接使用浏览器种缓存的数据。 其它状态信息
statusText
返回响应的HTTP状态说明,status 值为 200 时 statusText为 "OK"
readyState
返回一个当前XHR对象所处的活动状态
值状态描述
0
UNSENT
代理被创建,但尚未调用 open() 方法。
1
OPENED
open() 方法已经被调用。
2
HEADERS_RECEIVED
send() 方法已经被调用,并且头部和状态已经可获得。
3
LOADING
下载中;响应体部分正在被接收 responseText 属性已经包含部分数据。
4
DONE
下载操作已完成。
onreadystatechange
当 readyState变化时会触发次事件函数,如果使用 abort() 取消请求则次事件函数不会被触发
1 xhr.onreadystatechange = function () {
2 if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
3 console.log(xhr.responseText)
4 }
5 }
参考资料
兼容性
封装 XMLHttpRequest 对象
1 // 创建 构造函数
2 function Ajax(obj) {
3 this.url = obj.url ||'';
4 this.type = obj.type || 'get';
5 this.data = obj.data ||{};
6 this.success = obj.success || null;
7 this.error = obj.error || null;
8 }
9 // 原型上创建方法支持 post 和 get
10 Ajax.prototype.send = function(){
11 var self = this;
12 var toStr = Object.prototype.toString;
13 if (self.data === null && typeof self.data !== 'object' && Array.isArray(obj)) return;
14 return (function(){
15 // 实例化 XML对象
16 var xhr = new XMLHttpRequest();
17 var data = '';
18 // 序列化参数
19 for (var k in self.data){
20 data += k + '=' + self.data[k] + '&';
21 }
22 data = data.substr(0,data.length - 1);
23 // 接收回调函数
24 xhr.onreadystatechange = function(){
25 if (xhr.readyState === 4){
26 if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
27 isFunction(self.success) && self.success(xhr.responseText)
28 }else{
29 isFunction(self.error) && self.error(xhr)
30 }
31 }
32 }
33 // 初始化请求
34 if(self.type.toLocaleLowerCase() === 'post'){
35 xhr.open ('post',self.url,true)
36 // 设置请求头
37 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
38 //发送请求
39 xhr.send(data)
40 } else {
41 xhr.open('get', self.url + "?" + data,true)
42 xhr.send(null)
43 }
44 }());
45 };
46
47 function isFunction(obj){
48 return toStr.call(obj) === "[object Function]"
49 }
50
51 var ajax = new Ajax({
52 type:'post',
53 url:"/login",
54 data:{
55 loginname:"admin",
56 password:"admin"
57 },
58 success:function(e){
59 console.log(e)
60 },
61 error:function(err){
62 console.log(err)
63 },
64 }).send();
XMLHttpRequest Level 2 相比于 老版本的 XMLHttpRequest 新增以下内容:
可以设置 HTTP 请求超时时间
1 var xhr = XMLHttpRequest();
2 xhr.open('GET'.'url');
3 // 超时 2s
4 xhr.timeout = 2000;
5 // 超时处理
6 xhr.ontimeout = function(e) {
7 console.log(e)
8 }
9 xhr.send(null)
可以通过 FormData 发送表单数据
1 // 实例化 FormData
2 var formData = new FormData();
3 // 添加数据
4 formData.append(key,value);
5
6 xhr.open('POST','url');
7 xhr.send(formData);
可以上传文件
FormData 除了可以添加字符串数据,也可以添加 blob、file 类型的数据,因此可以用于上传文件。
在浏览器中,一般是通过文件上传输入框来获取 file 对象,比如:
1
1 document.getElementById('upload-file')
2 .addEventListener('change', function () {
3
4 var formData = new FormData();
5 // 获取数据
6 formData.append('uploadFile', this.files[0])
7 xhr.send(formData)
8 })
支持跨域请求
浏览器默认是不允许跨域请求的,有时候又是必要的,在以前通常使用JSONP来解决(IE10 以下不支持)
为了标准化跨域请求, W3C提出 跨域资源共享(CORS)前端无须修改代码,只需 服务器返回 Access-Control-Allow-Origin 响应头,指定允许对应的域
CORS 默认不发送 cookie 如果需要发送,前端需要设置 withCredentials 属性,同时服务器需要 返回 Access-Control-Allow-Credentials: true,
xhr.withCredentials = true;
检测XHR是否支持CORS最简单的方式,就是检查是否存在 `withCredentials`,再检测`XDomainRequest` 对象是否存在,即可兼顾所有浏览器
1 let createCORSRequest = (method,url)=>{
2 let var xhr = mew XMLHttpRequest();
3 if ('withCredentials' in xhr){
4 xhr.open(method,url,true);
5 }else if(typeof XDomainRequest != 'undefined'){
6 xhr = new XDomainRequest();
7 xhr.open(method,url);
8 }else{
9 xhr = null
10 }
11 return xhr
12 }
13 let request = createCORSRequest('get','baidu.com')
14 if(request){
15 request.onload = function(){
16 // request.responseText
17 }
18 request.send()
19 }
Preflighted Requests:
- 一个透明服务器验证机制,用于检查服务器是否支持[CORS](http://www.ruanyifeng.com/blog/2016/04/cors.html)
这是一个 OPTIONS 请求,使用了三个请求头
- Access-Control-Request-Method:请求自身使用的方法
- Access-Control-Request-Headers:自定义头部信息,多个头部以逗号分隔
- Origin报头:和简单请求相同,将请求的域发送给服务端,服务端再Access-Control-Allow-Origin 响应头中返回同样的域即可解决跨域问题。
img src特性:
- 一个网页可以从任何网页中加载图像,不用担心跨域问题,通过onload 和 onerror 事件处理确定是否接收到响应
- 请求的数据通过查询字符串形式发送,响应可以是任意内容,通常是像素图或204响应。
- 只能发送 get 请求,无法访问服务器的响应文本
1 let img = new Image();
2 img.onload = function (){
3 console.log('done')
4 }
5 img.src = 'http://www.baidu.com?test=test1'
可以获取服务端二进制数据
1. 使用 overrideMimeType 方法覆写服务器指定的 MIME 类型,从而改变浏览器解析数据的方式
1 // 参数 MIME 类型
2 // 告诉浏览器,服务器响应的内容是用户自定义的字符集
3 xhr.overrideMimeType('text/plain; charset=x-user-defined');
4 // 浏览器就会将服务器返回的二进制数据当成文本处理,我们需要做进一步的转换才能拿到真实的数据
5 // 获取二进制数据的第 i 位的值
6 var byte = xhr.responseText.charCodeAt(i) & 0xff
"& 0xff" 运算 参考 阮一峰的文章
2.xhr.responseType 用于设置服务器返回的数据的类型,将返回类型设置为 blob 或者 arraybuffer,然后就可以从 xhr.response 属性获取到对应类型的服务器返回数据。
1 xhr.responseType = 'arraybuffer'
2 xhr.onload = function () {
3 var arrayBuffer = xhr.response
4 // 接下来对 arrayBuffer 做进一步处理...
5 }
可以获取数据传输进度信息 参考资料
使用 onload 监听了一个数据传输完成的事件。
1 // 上传进度监听
2 xhr.upload.addEventListener('progress', onProgressHandler, false);
3
4 // 传输成功完成
5 xhr.upload.addEventListener('load', onLoadHandler, false);
6 // 传输失败信息
7 xhr.upload.addEventListener('error', onErrorHandler, false);
更多资料参考: 阮一峰的文章 MDN
AXIOS