上篇文章我们说到了什么是JSONP以及JSONP的用法,但说到底,JSONP终究只是一种以脚本的形式来运行的方法,并且,它只能向服务器发送GET请求,原则上不能发送其他如POST、PUT的请求的。因此在这里向大家隆重推出AJAX。
什么是AJAX,我们可以先把这四个英文字母拆分开来,他们分别代表的意思为:A(Asynchronous——异步的)、J(JavaScript)、A(And)、X(XML)
简单地说一下AJAX的起源和背景:IE5时率先在JavaScript中引入了ActiveX对象(API),使得JavaScript可以直接发起HTTP请求,随后被多个浏览器跟进和模仿后,最后W3C给这个API取名为XMLHttpRequest,并正式纳入W3C规范。
顺便提一下,在XMLHttpRequest纳入W3C规范的一年后,Google利用这个API推出了Gmail(不用刷新整个网页,是局部更新的),在当时惊动了整个互联网。
AJAX的技术特点:
1.使用XMLHttpRequest来发送请求。
2.服务器返回XML格式的字符串,返回XML格式的原因是因为当时只有XML是比较容易做到的一种数据传输格式,在这之后基本都开始使用JSON格式了。
3.JavaScript解析XML,并局部更新网页。
话不多说,我们直接来看看这个API是怎么样使用的:
let request=new XMLHttpRequest() request.open('GET','/xxx') //这里可以通过改变第一个参数,来发送PUT、GET、POST和DELETE请求request.onreadystatechange=()=>{ console.log(request.readyState) } request.send()
上述代码中,打印出来的readyState表示请求的状态码,每次发起请求后,状态码都会依次从0~4经过,每个状态码的具体代表的含义请看MDN
在0~4状态码中,2和3状态码由于存在时间很短,因此很难捕捉得到,而状态码4表示整个请求过程已经完毕,响应已经下载完成。之后我们可以通过let string=request.responseText
来获取响应的内容,这里值得注意的是,获取到的响应的内容永远只能为字符串。
当然我们不是一定可以获取到响应内容的,因为请求是存在失败的可能的,因此我们可以通过request.status
来获取HTTP的状态码来判断请求的情况。
if (request.readyState===4){ console.log('请求响应都已经完毕了') if (request.status>=200 && request.status<300){ console.log('说明请求成功') } else if (request.status>=400){ console.log('说明请求失败') } }
另外,XMLHttpRequest是可以自主设置请求头的,使用XMLHttpRequest.setRequestHeader(key,value)
即可,这是设置HTTP请求头部,即请求的第二部分的办法,此方法必须在open()方法和send()方法之间调用。如果同一请求头设置多次,则会合并成一个多个值的请求头,这里值得注意的是,一些不安全的请求头是不能设置的,这些请求头有哪些,需要读者自己去尝试~
这样看下来,好像AJAX很好用嘛,通过简单的API操作,就可以达到发送请求和获取响应的目的,比起JSONP来说,的确是方便了很多,但是,AJAX也有着一个突出的特点:正常情况下,无法跨域发送请求!!!
AJAX会被同源策略所限制(同源策略的具体介绍请点击此处阅读),跨域的JavaScript不能发送AJAX请求,只有“协议+端口+域名”完全一模一样的情况下才允许发送AJAX请求。
1.http://baidu.com 不可以向 http://www.baidu.com 发送AJAX请求
2.http://baidu.com:80 不可以向 http://baidu.com:81 发送AJAX请求
之所以说这是AJAX的“特点”,而不是“缺点”,这是因为如果没有了同源策略的限制,就没有任何隐私可言了,因为若没有了这种限制,像支付宝余额、QQ空间的好友列表这些信息均可以被他人读取,而之所以form表单这些可以跨域发送请求,那是因为他们会刷新页面,刷新了页面,旧的页面就不存在了,这样就能保证用户的隐私了。因此AJAX的这种“特点”的存在,十分有必要,也十分重要。
可是,有的时候我们依然需要发送跨域请求,但我们又想使用AJAX,怎么办呢?
那就使用CORS吧~CORS的全称是Cross-Origin Resource Sharing,具体的使用方法是,在跨域请求的服务器端写入以下代码
response.setHeader('Access-Control-Allow-Orign','要跨域访问的网址')
这样该网址就能跨域向该服务器发起AJAX请求了~
那么接下来进入我们的下一个重头戏,模拟JQuery封装AJAX的过程
window.jQuery.ajax=function({url,method,body,successFn,errorFn,headers}){ let request=new XMLHttpRequest() request.open(method,url) request.onreadystatechange=function(){ if (request.readyState===4){ if (request.status>=200 && request.status<300) { successFn.call(undefined,request.responseText) } else { errorFn.call(undefined,request) } } } request.send(body) } $('#button').on('click',function(){ window.jQuery.ajax( 'http://127.0.0.1:8888/pay', 'get', null, (responseText)=>{ console.log(responseText) let string=responseText let object=window.JSON.parse(string) amount.textContent=object.left alert('请求成功') }, (request)=>{ console.log(request) alert('请求失败') }, null ) })
以上是对AJAX的普通封装,以上封装存在着两个很明显的缺点:
1.每个参数的位置是确定的,因此难以记住,容易混淆。
2.GET请求一般没有body参数,只能用null代替。
那么我们先看如何改进第一个问题,解决方法:传入一个对象
window.jQuery.ajax=function(options){ let method=options.method let url=options.url ... } $('#button').on('click',function(){ window.jQuery.ajax({ url:'http://127.0.0.1:8888/pay', method:'get', succeessFn:(responseText)=>{ console.log(responseText) let string=responseText let object=window.JSON.parse(string) amount.textContent=object.left alert('请求成功') }, errorFn:(request)=>{ console.log(request) alert('请求失败') } }) })
但这样写代码太丑了,每次传进一个对象,都要把对象的键対值重新抽出来赋值,于是我们想到使用ES6的解构赋值的技巧:
let method=options.methodlet body=options.bodylet successFn=options.successFn//等价于let {method,body,successFn}=options
于是乎代码就变成了:
window.jQuery.ajax=function({url,method,body,successFn,errorFn,headers}){ ... } $('#button').on('click',function(){ window.jQuery.ajax({ url:'http://127.0.0.1:8888/pay', method:'get', succeessFn:(responseText)=>{ console.log(responseText) let string=responseText let object=window.JSON.parse(string) amount.textContent=object.left alert('请求成功') }, errorFn:(request)=>{ console.log(request) alert('请求失败') } }) })
再把这个封装升级一下,使它满足 Promise 规则:
window.jQuery.ajax=function({url,method,body,successFn,errorFn,headers}){ return new Promise(function(resolve,reject){ let request=new XMLHttpRequest() request.open(method,url) request.onreadystatechange=function(){ if (request.readyState===4){ if (request.status>=200 && request.status<300) { resolve.call(undefined,request.responseText) } else { reject.call(undefined,request) } } } request.send(body) }) } $('#button').on('click',function(){ window.jQuery.ajax({ url:'http://127.0.0.1:8888/pay', method:'get' }).then( (responseText)=>{ console.log(responseText) let string=responseText let object=window.JSON.parse(string) amount.textContent=object.left alert('请求成功') }, (request)=>{ console.log(request) alert('请求失败') } ) })
好了,大功告成!
看到这里,相信你已经懂得如何使用原生JS来使用和封装AJAX了。那么我们这次的文章也就介绍到这里了。
最后声明一下,封装过程一些属性的命名和jQuery中所封装的AJAX里的属性的命名可能并不一致,因此读者在使用jQuery封装下的AJAX要以其官网文档内声明的属性为准~
再次预告一下~下一次的文章将为大家讲述:JSONP的安全问题你了解过吗?
作者:宣泽彬
链接:https://www.jianshu.com/p/4d371de8e8ff