AJAX概述
这个技术涉及发送服务器请求额外数据而不刷新页面,从而实现更好的用户体验,改变了WEB诞生以来就一直延续的传统单击等待的模式。
XHR为发送服务器请求和获取响应提供了合理的接口,可以实现从服务器获取额外数据,意味着用户点击不用页面刷新也可以获取数据。
已经过时。
客户端请求的基本操作:
使用XHR对象首先调用open()方法,接受三个参数:请求类型、请求URL、表示请求是否异步的布尔值(true为异步)。调用open()不会实际发送请求,只是为发送请求做准备。
注意:只能访问同源URL
send()方法接受一个参数,是作为请求体发送的数据。如果不需要请求体,则必须传null,因为这个参数在某些浏览器中是必需的。调用send()之后,请求就会发送到服务器。
收到响应后,XHR对象的以下属性会被填充上数据:
- responseText:作为响应体返回的文本
- responseXML:如果相应内容类型是'text/xml'或'application/xml',那就是包含响应数据的XML DOM文档。
- status:相应的HTTP状态
- statusText:相应的HTTP状态描述
同步请求实例:
const xhr = new XMLHttpRequest()//初始化
xhr.open('GET','http://localhost:3000/server',false)//设置url和请求类型
xhr.send(null)//发送请求
if((xhr.status >=200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText)
} else {
alert('request was unsussessful: ' + xhr.status)
}
多数情况下请使用异步请求,这样可以不阻塞JS代码的执行。
XHR对象有一个属性:readyState
,表示当前处在请求/响应的哪个阶段。 从 0 到 4 发生变化:
- 0(Uninitialized): 请求未初始化,尚未调用open()方法
- 1(Open): 已调用open(),未调用send()方法
- 2(Sent): 请求已发送,尚未收到响应
- 3(Receiving): 已经收到部分响应
- 4(Complete): 已经收到所有响应
每次readyState
属性改变,都会触发readystatechange
事件。为保证跨浏览器兼容,onreadystatechange
事件处理程序应该在调用open()
之前赋值。
异步请求例子:
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status ==304){
alert(xhr.responseText)
} else {
alert('error')
}
}
}
xhr.open('get','example.php',true) //异步请求
xhr.send(null)
HTTP头部
默认情况下,XHR请求会发送以下头部字段:
- Accept:浏览器可以处理的内容类型
- Accept-Charset:浏览器可以显示的字符集
- Accept-Encoding:浏览器可以处理的压缩编码类型
- Accept-Language:浏览器使用的语言
- Connection:浏览器与服务器的连接类型
- Cookie:页面中设置的Cookie
- Host:发送请求的页面所在的域
- Referer:发送请求的页面的URI
- User-Agent:浏览器的用户代理字符串
如果需要发送额外的请求头部,可是使用setRequestHeader()
方法,接受两个参数,头部字段的名称和值,必须在open()
之后,send()
之前调用。
可以使用getResponseHeader()
方法从XHR对象获取响应头部,传入参数为要获取的头部的名称。
使用getAllResponseHeaders()
方法取得所有响应头部,这个方法返回包含所有响应头部的字符串
设置请求参数:
xhr.open('GET','http:localhost:3000/server?a=100&b=200&c=300',true)
发送post请求:
function submitData(){
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){
if(xhr.readystate == 4){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText)
} else {
alert('request was unseccessful')
}
}
}
xhr.open('post', 'postexample.php', true)
xhr.setRequestHeader('Connect-Type','application/x-www-form-urlencoded')
let form = document.getElementById('user-info')
xhr.send(serialize(form)) // 需要序列化
}
服务端也需要有post请求的设置
app.post('/server',(request, response)=>{
//设置允许跨域
response.setHeader('Access-Control-Allow-Origin','*')
response.send('hello express post')
})
post设置请求体:
在send()中设置参数:
xhr.send('a=100&b=100&c=100');
查看:
可以是任意格式、任意内容:
xhr.send('a:100&b:100&c:100');
但是需要服务端有对应的处理。
设置请求头信息
在xhr.open()后调用setRequestHeader('','')方法:
xhr.setRequestHeader('Content-Type','application/x-www-formurlencoded');
效果:
可以添加自定义的请求头,但需要服务器代码进行一些设置。
XMLHttpRequest Level 2
对XHR对象的进一步发展:
1.formData类型,便于表单序列化
let data = new FormData()
data.append('name', 'Nicholas')
// 也可以直接给FormData构造函数传入一个表单元素
let data2 = new FormData(document.forms[0])
let form = document.getElementById('user-info')
xhr.send(new FormData(form))
2.超时
IE8给XHR增加了一个timeout
属性,发动请求后等待一段时间,如果相应不成功就中断请求,调用ontimeout
事件处理程序。
...
xhr.open('get', 'timeout.php', 'true')
xhr.timeout = 1000
xhr.ontimeout = function(){
alert('request did not return in a second')
}
xhr.send(null)
服务端响应json数据
服务端能传输的只有字符串和buffer,所以需要用JSON.stringify()方法转换成字符串
app.all('/json-server',(request, response)=>{
//设置允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
response.setHeader('Access-Control-Allow-Headers','*');
const data = {
name:'hhh'
};
//继续字符串转换
let str = JSON.stringify(data);
response.send(str)
})
前端收到的数据是字符串形式,有两种方法转换,手动转换和自动转换。
手动转换:
const data = JSON.parse(xhr.response)
console.log(data);
result.innerHTML = data.name
自动转换: 初始换xhr之后设置响应体数据类型:
xhr.responseType = 'json'
这样直接拿到的response就是转换过的数据
result.innerHTML = xhr.response.name
IE缓存问题
给URL添加一个唯一的ID
xhr.open('GET','http://127.0.0.1:8000/ie?t='+Date.now())
xhr.open('GET','http://127.0.0.1:8000/ie?t='+Math.random())
请求超时、网络故障、取消请求
请求超时设置:
xhr.timeout = 2000;//设置超时时间
xhr.onTimeout = function(){//绑定超时回调函数
alert('请求超时')
}
网络故障设置:
xhr.onerror = function(){//网络故障回调
...
}
取消网络请求:
xhr.abort();
请求重复发送的问题
我对问题的理解:如果用户在短时间内重复多次发送网络请求,会对 服务器造成负担,通过解决这个问题,可是使同一时刻只有一个网络请求在向服务器发送请求。(个人理解) 使用标识变量
let x = null;
let flag = false;
btn.onclick = function(){
if(flag) x.abort();//如果正在发送就取消上一个请求,创建一个新的请求
x = new XMLHttpRequest();
flag = true;
...
x.onreadystatechange = function(){
if(x.readyState === 4){
flag = false;//响应后修改标识变量
...
}
}
}
JQuery中的AJAX
GET方法
$.get(url,[data],[callback],[type])
data:携带参数
callback:载入成功的回调函数
type:设置返回内容格式,'xml','html','json','text','script'
例:
<script>
$.(button).eq(0).click(function(){
$.get('http://127.0.0.1:8000/jqury-server',
{a:110,b:200},
function(data){
console.log(data);
},
'json')
})
</script>
POST方法
$.get(url,[data],[callback],[type])
例:
$.(button).eq(1).click(function(){
$.post('http://127.0.0.1:8000/jqury-server',
{a:110,b:200},
function(data){
console.log(data);
},
'xml')
})
通用方法
$.(button).eq(2).click(function(){
$.ajax({ //参数是一个对象
url:'http://127.0.0.1:8000/delay',
data:{a:100,b:200}, //url参数
type:'GET', //请求类型
dataType:'json', //响应体结果类型
timeout:2000, //超时时间
succecc:function(data){ //成功回调
console.log(data);
}
error:function(){ //失败回调
console.log('ERROR!')
},
headers:{ //添加头信息
c:300;
d:400
}
})
})
自定义程度强,参数内的对象有很多可设置的内容。
Axios发送AJAX请求
get请求:axios.get(url[,config])
第二个参数是一个设置参数的对象
axios.defaults.baseURL = 'http:127.0.0.1:8000'; //设置默认路径
btns[0].onclick = function(){
axios.get('/axios/server',{ //第二个参数是一个对象
params:{ //参数
id:100,
id2:111
},
headers:{ //添加的请求头信息
name:'aaa',
age:99
}
}).then(value => { //基于promise设置回调函数
console.log(value);
});
)
}
post请求:axios.post(url[,data[,config]])
第二个参数是请求体,第三个参数是其他配置
btns[1].onclick = function(){
axios.post('/axios/server',{ //第二个参数---请求体-一个对象
usrname:'admin',
password:'hhh'
},
{ params:{ //第三个参数---其他配置-一个对象
id:200,
id2:11
}
headers:{ //请求头参数
name:'aaa',
age:99
}
}).then(value => {
console.log(value);
});
)
}
axios函数发送请求(通用方法)
btn.onclick = function(){
axios({
method:'post', //请求方法
url:'http://bit.ly/2mTM3nY',
responseType:'stream',
params:{ //url参数
id:100,
id2:111
}
headers:{ //请求头
a:100,
b:200
}
data:{ //请求体参数
username:'admin',
password:'hhh'
}
})
.then({
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});//这个是github中的函数,下面是尚硅谷视频中的
//或者是下面的函数
.then(response => { //返回的数据是已经解析过的
console.log(response);
console.log(response.status); //响应状态码
console.log(response.statusText); //响应状态字符串
console.log(response.headers); //响应头信息
console.log(response.data); //响应体
})
};
使用全局函数fetch
fetch(input[,init]);
input:url
init:可选,一个配置项对象,包括所有请求的设置。可选参数:
- method:请求方法,'GET','POST'
- headers:请求头信息
- body:请求体
btn.onclick = function(){
fetch('http://127.0.0.1:8000/fetch-server?vip=10',{ //url参数直接写在url中
method:'POST',
headers:{
name:'hhh'
},
body:'username=admin&password=admin' //POST请求设置请求体
}).then(response => {
return response.text(); //json类型数据使用response.json()
}).then(response => {
console.log(response);
})
}
同源策略
一种安全策略。
同源:协议、域名、端口号 完全相同
如何解决跨域
JSONP(只支持GET请求)
通过script 标签的跨域能力实现跨域发送请求,请求的路径作为script的src属性。
原理:在网页中哟一些标签天生具有跨域能力,比如:img,link,iframe,script。
使用jsonp实现跨域步骤:
- 动态创建一个script 标签
- 设置script标签的src属性
- 将script标签插入文档中
功能实现部分相关代码
input.onblur = () => {
let username = this.value;
//向服务器发请求
//创建一个script标签
const script = document.createElement('script');
//设置script标签的src属性
script.src = 'http:127.0.0.1:8000/check-username';
//将script插入文档中
document.body.appendChild(script)
}
服务器发送的响应内容应该是js代码段,而不是简单的数据或字符串
app.all('/check-username',(request,response) => {
const data = {
exist:'1',
msg:'用户名已存在'
};
//将数据转换成字符串
let str = JSON.stringify(data);
//返回结果
response.end(`handle(${str})`);
})
上面部分是服务器端的代码,代码响应部分是
response.end(`handle(${str})`);
handle函数在html页面中定义:
function handle(data){
input.style.border = 'solid 10px #456';
p.innerHTML = data.msg;
}
jquery发送jsonp请求
发送请求:
- url中一定要有callback参数。
- jquery自动注册一个callback函数,服务器端也能接收,用
request.query.callback
获取。 - 将callback的值作为函数调用的函数拼接字符串(个人理解:不再需要在使用html中handle()函数对返回结果处理,可以直接得到返回结果——可以只有数据)
$('button').eq(0).click(function(){
$.getJSON('http:127.0.0.1:8000/jquery-jsonp-server?callback=?',
function(data){
$('#result').html(`
名字:${data.name},<br/>
城市:${data.city}
`)
})
})
服务器响应:
app.all('/jquery-jsonp-server',(request,response) => {
const data = {
name:'hhh',
city:['beijing','shanghai']
};
//将数据转换成字符串
let str = JSON.stringify(data);
//接受callback参数
let cb = request.query.callback
//返回结果;
response.end(`${cb}(${str})`);
})
设置CORS响应实现跨域
CORS:跨域资源共享
在服务器端对响应的请求头进行设置
必须字段:
Access-Control-Allow-Origin:表示服务端允许的请求源,*标识任何外域,多个源 , 分隔
可选字段
Access-Control-Allow-Credentials:false 表示是否允许发送Cookie,设置为true
同时,ajax请求设置withCredentials = true,浏览
器的cookie就能发送到服务端
Access-Control-Expose-Headers:调用getResponseHeader()方法时候,能从header中获
取的参数
更多关于CORS的知识:阮一峰——跨域资源共享 CORS 详解 www.ruanyifeng.com/blog/2016/0…