AJAX简介
Ajax:是异步的JS和XML、通过AJAX可以在浏览器中向服务器发送【异步请求】,最大的优势:页面无刷新获取数据。
AJAX的优点:
1.可以无需刷新页面,就可以与服务器端进行通信。
2.允许你根据用户事件,来更新部分页面的内容。
AJAX的缺点:
1.没有浏览历史,不能回退。
2.存在跨域的问题。
3.SEO(搜索引擎优化)不太友好。
SEO:爬虫不能爬取AJAX请求的数据,但是可以通过很多其他手段获取到。
初始AJAX
XMLHttpRequest:AJAX的所有操作都是通过这个对象来进行的。
请求八大请求,常用的主要有:GET、POST、PUT、DELETE
点击按钮,发送请求
<button id = "btn">点我发送请求</button>
<div id = "content"></div>
<script>
//获取按钮
const btn = document.querySelector('#btn')
//给按钮绑定监听
btn.addEeventListener('click',()=>{
//1.创建xhr实例对象(new)
const xhr = new XMLHttpRequest()
//1.5:当状态发生改变时,状态码
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
//readyState为4时,数据完美响应
console.log(xhr.response)
content.innerHTML = xhr.response
}
}
//2.指定发送请求的url、method
xhr.open('GET','http://localhost:8080/test_get')
//3.发送请求
xhr.send()
})
</script>
xhr实例对象通过open()来指定【请求方法】和【url】,然后通过send()来发送请求。
跨域问题:本地的ip地址是:127.0.0.1(本地回环地址)和【localhost】意义相同。
一般是 ip地址+端口号组成,我们的项目是:127.0.0.1:5500/src/项目.html,但是我们请求的地址是:localhost:8080,端口号不同,就跨域了。(主机名不一样也是跨域,后期我们再说)。
xhr的5种状态
xhr有5种readyState(状态),值分别为:0、1、2、3、4(最重要);
当创建请求--》收到请求,状态码会发生4次变化:0--1、1--2、2--3、3--4;
当xhr=0时,表示【实例化】的那一刻。0也叫初始状态。
当xhr=1时,open()已经调用了,但是send()还没有调用,此时可以修改请求头内容。
//设置请求头
if(xhr.readyState === 1){
xhr.setRequestHeader('demo',123)
}
当xhr=2时,send()已经调用了,已经无法修改请求头内容。
当xhr=3时,已经回来一部分数据了,小的数据会在此阶段一次性接收完毕,较大的数据有待进一步接收。(响应头一定会回来的xhr.getResponseHeaders() / xhr.getResponseHeader('content-length'))
当xhr=4时,表示数据全部都完美的回来了。
一般在xhr.readyState()中还会附加上一个条件:http是有状态码的。
if(xhr.readyState === 4 && (xhr.status >= 200)&&(xhr.status < 300)){
console.log(xhr.response)
}
ajax的get请求
btn.addEventListener('click',()=>{
//1.创建xhr实例对象
const xhr = new XMLHttpRequest()
//1.5绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status>=200&&xhr.status<300){
console.log(xhr.response)
}
}
}
//2.设置 get请求传递参数 和 url
xhr.open('GET','http://127.0.0.1:8080/test_get?name=老刘&age=18')
xhr.open('GET','http://127.0.0.1:8080/test_get/老刘/18')
//3.发送请求
xhr.send()
})
GET请求有两种参数:query('...?name=tom&age=18'),params('.../老刘/18');get请求没有请求体。
query:用的是urlencoded编码格式,形如:key=value&key=value
param:形如:/xx/xxx/老刘/18
设置参数后,后端就可以请求到参数,param需要设置参数占位,来接收
ajax的post请求
btn.addEventListener('click',()=>{
//1.实例化
const xhr = new XMLHttpRequest()
//绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState===4){
if(xhr.status>=200&&xhr.status<300){
console.log(response)
}
}
}
//post请求
xhr.open('POST','http://127.0.0.1:8080/test_post')
xhr.open('POST','http://127.0.0.1:8080/test_post?name=tom&age=18')
xhr.open('POST','http://127.0.0.1:8080'/test_post/tom/18')
//请求体参数 发送
xhr.send()
})
POST请求可以携带:query参数、params参数、body参数(请求体参数)
但是query和params不是经常用,在post请求中,我们常用的是boby请求体参数!
请求体参数:写在xhr.send()中,并且以urlencoded形式,也就是key=value&key=value的形式。
或者以json格式进行传参
xhr.open('POST','http://127.0.0.1:8080/test_post')
---------------------------------------
//这个一定要写(urlencoded),追加响应头用于标识携带请求体参数的编码格式
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
xhr.send('name=老刘&age=18')
----------------------------------------
//或者json,携带json编码形式的请求体参数
xhr.setRequestHeader('Content-type','application/json')
const person = {name:'老刘',age:18}
xhr.send(JSON.stringify(person))
请求体:
xhr.responseType = 'json':收到响应的是json格式的数据时,自动转换为JSON对象格式,不再需要手动修改。
回顾解构赋值
let obj = {a:1,b:{c:2}}
//标准的解构赋值写法
const {c} = obj.b
//连续写法,可以直接拿到{b},但是我要c,所以要连续写法
const {b:{c}} = obj
//连续解构赋值+重命名
const {b:{c:value}} = obj
console.log(value)//2
重命名:{x:__重命名__},此时key对应的value的名字就被修改了。
请求异常与超时的处理
btn.addEventListener('click',()=>{
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status>=200&&xhr.status<300){
console.log(response)
}
}
}
xhr.open('GET','http://127.0.0.1:8080/get_person')
//responseType指定返回数据的格式
xhr.responseType = 'JSON'
//一旦请求出错,就会调用这个回调函数
xhr.onerror =() =>{
console.log('请求出错啦')
}
//设置超时时间,单位是:毫秒
//我等了你2s,如果没有请求/响应,直接抛弃这个请求
xhr.timeout = 2000
xhr.ontimeout =()=>{
cnosole.log('网速不行,请切网重试')
}
xhr.send()
})
xhr.onerror = ()=>{}:一旦出错,就会调用这个回调函数。
xhr.timeout:设置超时时间,单位是毫秒
xhr.ontimeout = ()=>{}:一旦超过我所设置的超时时间,就会调用这个回调函数。
取消请求
程序员设置了超时时间,但是还没有触发到超时时间,用户缺主动不想要了,这个时候就需要设置一个取消请求。
<button id = "btn2">取消请求</button>
const btn2 = document.querySelector('#btn2')
btn2.addEventListener('click',()=>{
const xhr = new XMLHttpRequest()
//用户主动取消
xhr.abort()
})
通过xhr.abort()方法,来取消请求。
如果:
xhr.send()
xhr.abort()
一发送,就取消;实际上,如果你请求的速度非常快,还是有一定几率会请求到数据的,如果请求的速度比较慢,那么可能被拦截取消了。
避免多次重复请求
//请求是否处于加载中
let isLoading
btn.addEventListener('click',function(){
//判断写在创建实例的上面,因为我第二次点击的时候,又创建了一个新的xhr对象;
//但是我需要判定的是我本次的,而不是新创建的xhr对象
if(isLoading){
xhr.abort()
}
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status>=200&&xhr.status<300){
//这里是数据响应,设置false,说明数据已经收到响应的了
//代表已经发送过一次请求了
isLoading = false
}
}
}
xhr.open(...)
xhr.send()
//数据发送后改为true,说明请求已经发送了,数据正在加载中,等待响应
isLoading = true
})
跨域与同源策略
为什么会有跨域这个问题?
答:因为浏览器为了自身的安全,而采用了同源策略
同源策略:
1.现在所有支持JavaScript的浏览器都会使用这个策略。
2.web是构建在同源策略的基础之上的,浏览器只是针对同源策略的一种实现。
3.同源是指:【协议】、【域名IP】、【端口】必须要完全相同,这样才能算是在同一个域中。
端口号不写,就用默认的端口号(80)。
补充:<iframe>:用来内嵌别人的一个网页(现在已经不支持了,大伙看个乐子就行)
非同源受到的限制:
1.Cookie不能读取。
2.DOM无法获得。
3.Ajax请求不能获取数据(请求能发过去,但是数据回不来)。
jsonp解决跨域
开发基本不用,但是面试老是问你。
JSONP是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来的,【只支持get请求】!!!
JSONP是怎么工作的?
1.在网页中,有一些标签、天生就具有跨域的能力,比如:img、link、iframe、script等。
2.JSONP就是利用了script标签发送请求,不受同源策略的限制特点。
3.同源策略只是限制了xhr、Cookie、DOM,所以标签可以跨域
哪些跨域发get请求:form表单、xhr、浏览器地址栏、<img src = '???'>、<script>;这些常见的带有外部html的发的都是get请求。
<button id = "btn">点我发送数据</button>
const btn = document.querySelector('#btn')
btn.addEventListener('click',()=>{
})
CORS后端解决跨域
官方解决跨域的方案,他的特点是:不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持所有常见的特点。
本质是:5500端口向8080端口发送ajax请求数据,8000端口收到请求后,附加上两个
响应头【Access-Control-Allow-Orgin】
响应头【Access-Control-Expose-Headers】,这个用来拿到返回的响应头,否则响应头在前端,就取不到了
写法:response.setHeader('响应头','响应给的ip地址')
这些工作都是后端做的,让5500端口可以访问
app.get('/test_get',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:5500')
response.setHeader('Access-Control-Expose-Headers','http://127.0.0.1:5500')
response.send('hello_test_get')
})
上方仅仅适用于get,post;对于put,delete这种繁琐的请求来说,不管用。
因为put、delete会先发送一次(嗅探请求)域请求(我先扫一遍代码看看有没有问题),然后才会发送一次正常请求。
解决:要多加一条options('put请求路径','put的回调'})
//域请求方式,路径要和put请求保持一致
app.options('/test_put',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Expose-Headers','*'))
//允许所有请求方式的请求头
response.setHeader('Access-Control-Allow-Methods','*')
}
---------------------------------------------
app.put('/test_put',(request,response)=>{
response.setHeader('Access-Control-Allow-Origin','*')
response.setHeader('Access-Control-Expose-Headers','*')
})
前端:
xhr.open('PUT','http://localhost:8080/test_put')
xhr.send()
如果你学了node.js,可以通过yarn add cors下载一个node中专门用来解决跨域的库
const cors = require('cors')
app.use(cors())
//告别手动书写响应头,并且不用书写options域请求
app.put('/test_put',(request,response)=>{
//不用再写了,会自动添加上
//response.setHeader('')
response.send('hello_test_put')
})