AJAX之前
回顾一下最早向服务器发请求的方式
-
表单发送,最早的发请求方式,可以发各种类型的请求,缺点是每次发送完请求都需要刷新页面,后来前辈们的做法是在iframe中刷新页面。
- 利用
标签,只能发送GET请求,页面会被刷新。
-
和
,只能发送GET请求,不过页面不会被刷新,但是必须以图片或者css, favicon的形式展示。
- 利用
标签,只能发送GET请求且页面不会刷新,必须以脚本形式运行。利用SRJ技术延伸出了JSONP技术,解决跨域请求的好办法。
上面几种方法是我们前端最常见的向后端发送请求的方法,它们或多或少都可以解决我们的需求,但是都不完美,我们没有办法在浏览器中随心所欲的向服务器发送请求。
有没有办法可以做到,我们可以发送任意类型的请求,以我们自己方式展示而不是img, css, script这些呢?
AJAX的出现
ActiveX
在AJAX正式登上舞台之前,那时还是IE的天下,微软首先做出了突破。
IE5率先引入了一个ActiveX对象,可以让JS直接发送HTTP请求。
AJAX
ActiveX出现后,Mozilla, Safari, Opera也随之模仿,引入了一个叫XMLHttpRequest的对象,后来被W3C纳入了规范。
那么AJAX到底是什么呢?
它跟JSONP一样,也是一种解决方案的名字。
Jesse James Garrett将下面这个技术取名为AJAX:异步的JavaScript和XML。
- 使用XMLHttpRequest发送请求
- 服务器返回XML格式字符串
- JS解析XML并更新局部页面
AJAX的使用
在使用AJAX之前,再回顾一下JSONP是怎么实现的吧,看看jQuery是怎么用的(注释的部分是原生JS实现)。
灵梦的塞钱箱: &&&money&&&
后台的代码也非常简单,获取一下callback然后将数据(”success”)通过callback传回去而已。
else if(path === '/pay'){
let callback = query.callback//从发送GET请求的查询参数获取callback名
response.statusCode = 200
response.setHeader('Content-Type', 'application/javascript; charset=utf-8')
let amount = fs.readFileSync("./money.db", "utf-8")
amount ++
fs.writeFileSync("./money.db", amount)
// response.write(`${callback}("scuess")`)
response.write(`${callback}("success")`)
response.end()
XMLHttpRequest
我们的AJAX的第一步是:使用XMLHttpRequest发送请求
那么就我们先来看看这个东西。
使用XMLHttpRequest非常简单,只需要三行代码就可以发送一个HTTP请求
pay.addEventListener("click", ()=>{
//这个对象有我们需要的所有信息
var request = new XMLHttpRequest();
request.open("GET", "/ajax")
request.send()
})
open可以接受五个参数,我们一般只填写前两个就够了,默认就是异步的(不是异步就不是AJAX了)。method可以使任意方法。
void open(
DOMString method,
DOMString url,
optional boolean async,
optional DOMString user,
optional DOMString password
);
XMLHttpRequest顾名思义最早是用来传XML数据的,虽然现在已经没人用了(大雾),但是为了尊重历史,我们也从XML开始用起好了,毕竟我们学的是技术,XML只是工具,万变不离其中。
现在我们来简单实现一下AJAX的第二步,服务器返回XML格式字符串。
else if (path === '/ajax') {
response.statusCode = 200
response.setHeader("Content-Type", "text/xml; charset=utf-8")
//这个xml是网上随便找的
response.write(`
Tove
Jani
Reminder
Don't forget me this weekend!
`)
response.end()
}
后台代码写好了,如果接收到访问/ajax的请求,向它返回一段XML。
接下来就可以轮到AJAX的第三步了,JS解析XML并更新局部页面。
现在回到我们的前端,就像最上面说的那样,XMLHttpRequest包含了我们需要的所有东西,当然也包括后台返回给我们response,我们只需要查看XMLHttpRequest对象的response属性就可以获取响应。
异步
我们直接调用response就可以了吗?
pay.addEventListener("click", ()=>{
var request = new XMLHttpRequest()
request.open("GET", "/ajax")
request.send()
console.log(request.responseText)
})
很遗憾,你的浏览器可能什么都不会返回给你。可是我们在F12中是能够看到我们的response的。
这是因为我们通过XMLHttpRequest发送请求的整个过程是异步的,从①这个request对象发起请求到收到请求与②我们查询request对象是同时进行的。
我们可以看一下它们花费的时间
console.time()
var request = new XMLHttpRequest()
request.open("GET", "/ajax")
request.send()
console.log(request.responseText)
console.timeEnd() // 结果在0.7~1 ms之间浮动
再看看我们获取到响应的时间,是远超我们查询时时间的。
那有什么办法可以等到浏览器接收到响应之后再获取响应内容呢?
当然有,XMLHttpRequest给我们提供了一个很便利属性和api。
readyState
XMLHttpRequest.readyState 属性返回一个 XMLHttpRequest 代理当前所处的状态。一个 XHR 代理总是处于下列状态中的一个:
0 代理被创建,但尚未调用 open() 方法。
1 open() 方法已经被调用。
2 send() 方法已经被调用,并且头部和状态已经可获得。
3 下载中; responseText 属性已经包含部分数据。
4 下载操作已完成。
我们只需关注这个readyState即可,当它的状态码为4的时候就可以获取到response了。可以通过一个简单的例子演示一下,我们每1ms访问一次readyState
let timer = setInterval(()=>{
console.log(request.response);
if(request.readyState === 4){
window.clearInterval(timer)
}
},1)
可以看到,实际上第五次访问时response加载完毕(这些浏览器提供的api还有setInterval都不是很精确的不用很在意,大雾- -!)。
XMLHttpRequest提供了一个便利的api,onreadystatechange事件,readyState的每一次变动都会被监听到
request.onreadystatechange = ()=>{
if(request.readyState === 4){
console.log("加载完毕")
var str = request.responseText
var xml = (new DOMParser()).parseFromString(str, "text/xml")
}
}
})
到这就实现了一个简单的怀旧版AJAX,之后就是对xml各种复杂的DOM操作了。
当然我们现代人现在用的都是JSON,不过道理都是一样。
AJAX的限制
基于安全问题的同源策略的限制,我们希望发送跨站请求时,是无法直接发送AJAX请求的。
最简单的方法就是使用CORS,我们只需要在后端的API上添加response header
Access-Control-Allow-Orign: "