概述
在不刷新页面的情况下更新页面的数据。(布局更新)
应用场景:
- 实现页面上拉加载更多数据
- 列表数据无刷新分页
- 表单项离开焦点数据验证(避免如邮箱重复等造成用户重新填写整个表单)
- 搜索框提示文字下拉列表
运行环境
Ajax技术需要运行在网站环境中才能生效,直接双击html文件运行是没有用的
实现步骤
1 创建Ajax对象
var xhr = new XMLHttpRequest();
2 告诉Ajax请求地址及请求方式
xhr.open('get','http://www.example.com')
3 发送请求
xhr.send();
4 获取服务器端给客户端给客户端响应的数据
xhr.onload = function(){
//...
console.log(xhr.responseText);
}
服务器端响应的数据格式
服务器端大多数情况下会以json对象作为响应数据的格式 ,客户端拿到数据后用DOM方法把数据添加到HTML页面中.
问题:服务端响应一个JSON对象时,前端拿到的是一个字符串,而不是JSON
,把JSON字符串转换为JSON对象的方法:
JSON.parse()
示例代码
xhr.onload = ()=>{
// console.log(xhr.responseText)
// console.log(typeof xhr.responseText)
var responseText = JSON.parse(xhr.responseText)
console.log(responseText)
var str = '<h2>'+responseText.name+'<h2>'
document.body.innerHTML = str
}
传递请求参数
-
传统表单网站 : 请求参数需要通过表单的方式来传递.
image.png
get请求的参数格式:
www.example.com?参数名称=参数值&参数名称=参数值....
-
Ajax中
get请求
客户端:
var btn = document.getElementById("btn")
var userName = document.getElementById('userName')
var age = document.getElementById('age')
btn.onclick = ()=>{
console.log(userName.value)
var xhr = new XMLHttpRequest()
var nameValue = userName.value
var ageValue = age.value
var params = "name="+nameValue+'&'+"age="+ageValue
xhr.open('get','http://localhost:3000/requestParameter?'+params)
xhr.send()
xhr.onload = ()=>{
console.log(xhr.responseText)
}
}
服务器端:
app.get('/requestParameter',(req,res)=>{
res.send(req.query)
})
post请求:
image.png
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
//格式为: www.example.com?name=xxx&age=xxx....
xhr.setRequestHearder('Content-Type','application/json')
//格式为json字符串
- 将JSON转换成字符串的方法:
JSON.stringify()
- 将JSON字符串转换成JSON的方法
JSON.parse()
Ajax状态码
ajax状态码onreadystatechange事件
该事件在ajax状态码改变时被触发
image.png
错误处理
1 网络畅通,服务器可以接收到请求,但是服务器端返回的结果不是预期结果
2 网络畅通,服务器端没有接收到请求,返回404状态码
3 网络畅通,服务器端能接收到请求,返回500状态码
4 网络中断,请求无法发送到服务器端
低版本IE浏览器中的缓存问题
在低版本的IE浏览器中,如果我们向服务器的同一个路由下再次发起请求,拿到的其实是上一次请求时的数据,这是因为在低版本的IE下,新的请求并没有发出,我们拿到的是浏览器的缓存数据
eg:
xhr.open('get','http://localhost:3000/IEcache?t='+Math.random())
同步异步概述
- 同步:一件事情做完才能做下一件事情
- 异步:一件事做一半可以先做后面的时,如AJAX,一般会有回调函数
Ajax封装
问题:发送一次代码过多,真实项目中往往需要发送多个请求,代码比较冗余。
解决方案:把请求代码封装到函数中,发送时调用函数即可。
eg:
截屏2020-02-07下午12.07.52.png
实际代码:
function ajax(options){
//转换参数格式,拼接
var params = ''
for(attr in options.paramsData){
params += attr + '=' + options.paramsData[attr] +'&'
}
params = params.substr(0,params.length-1) //截取掉最后的&
console.log(params);
//init ajax object
var xhr = new XMLHttpRequest()
//配置Ajax对象
if(options.type === 'get'){
options.url = options.url + '?' + params
}
xhr.open(options.type,options.url)
if(options.type === 'post'){
//设置请求参数格式的类型
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
xhr.send(params)
}else{
xhr.send()
}
//发送请求
//xhr.send()
//监听onload事件
xhr.onload = function(){
options.success(xhr.responseText)
}
}
ajax({
//请求方式
type:'post',
//请求地址
url:'http://localhost:3000/function',
paramsData:{
name:'zhangsan',
age:20
},
success:function(data){
console.log('hh'+data);
}
})
服务器端返回数据的时候,会在相应头中存放响应的数据类型,以供前端开发人员操作
xhr.getResponseHeader()
//获取相应头中的数据
还有,我们应该只强制要求用户传递几个必要参数,而没有传递参数的地方,应该留有默认值。
解决的办法,再函数中定义一个对象,用来存放默认值,如果用户传递了参数,就用用户传递的,如果用户没有传递,那么就取出默认值
完整版如下:
function ajax(options){
//声明默认参数
var defaults = {
type:'get',
url:"",
paramsData:'',
header:{
'Content-Type':'application/x-www-form-urlencoded'
},
success:function(){},
error:function(){},
}
//对象覆盖
Object.assign(defaults,options)
//转换参数格式,拼接
var params = ''
for(attr in defaults.paramsData){
params += attr + '=' + defaults.paramsData[attr] +'&'
}
params = params.substr(0,params.length-1) //截取掉最后的&
//console.log(params);
//init ajax object
var xhr = new XMLHttpRequest()
//配置Ajax对象
if(defaults.type === 'get'){
defaults.url = defaults.url + '?' + params
}
xhr.open(defaults.type,defaults.url)
if(defaults.type === 'post'){
//设置请求参数格式的类型
var contentType = defaults.header['Content-Type']
xhr.setRequestHeader('Content-Type',contentType)
//判断用户期望的请求参数格式的类型
if(contentType === 'application/json'){
xhr.send(JSON.stringify(defaults.paramsData))
}
else {xhr.send(params)}
}else{
xhr.send()
}
//发送请求
//xhr.send()
//监听onload事件
xhr.onload = function(){
var responseText = xhr.responseText
//查看服务器返还的数据的类型
var contentType = xhr.getResponseHeader('Content-Type')
if(contentType.includes('application/json')){//判断是否字符串包涵
//说明返还的是json字符串,需要转换称json
responseText = JSON.parse(responseText)
}
//需要判断请求是否成功
if(xhr.status == 200)
defaults.success(responseText,xhr)
else
defaults.error(responseText,xhr)
}
}
ajax({
//请求方式
type:'post',
//请求地址
url:'http://localhost:3000/function',
// paramsData:{
// name:'zhangsan',
// age:20
// },
// header:{
// 'Content-Type':'application/json'
// },
success:function(data,xhr){
console.log(data);
},
error:(data,xhr)=>{
console.log(data)
console.log(xhr);
}
})
Ajax请求限制
Ajax只能向自己的服务器发送请求,如果想要获取的数据是在别的服务器上的,就是不被允许的
什么是同源:
如果两个页面拥有相同的协议,域名和端口,那么这两个页面就属于同一个源,其中只有有一个不相同,就叫不同源。
不同源的网站之间是不允许发送Ajax请求的。
使用JSONP解决同源限制问题
JSONP是json with padding的缩写,它不属于Ajax请求,但是可以模拟出ajax的效果(需要前后端配合)
1 将不同源的服务器端请求地址鞋子script标签的src属性中
就好比,用script标签引入jquery,就是从外部服务器获取的数据
2 服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数
eg:
const data = 'fn({name:"zhangsan",age:"14"})';
res.send(data)
3 在客户端全局作用域下准备好函数的定义,且要写在script标签的前面
function fn(data){}
4 在函数内部对服务器返回的具体数据进行处理
动态发送jsonp:用JS代码生成JS标签,然后追加到页面当中。
优化:应该想办法封装jsonp的代码
一个封装后的jsonp函数参考
function jsonp(options){
//动态创建script标签
var script = document.createElement('script')
//生成随机的函数名 math.random生成的是0-1的随机小数,把小数点替换成空
var fnName = 'myJsonp'+Math.random().toString().replace('.','')
//他已经不是全局函数了,要想办法把它变成全局的(options.success)
window[fnName] = options.success
var params = ''
for(var attr in options.data){
params+='&'+attr+'='+options.data[attr]
}
//为标签添加src属性
script.src = options.url + '?callback='+fnName+params
//把标签追加到页面
document.body.appendChild(script)
script.onload = function(){
document.body.removeChild(script)
}
}
如果服务是express启动的,可以直接这样返回请求数据
:
res.jsonp({name:'lisi',age:13})
$.ajax()
这是Jquery封装好的方法
作用:发送ajax请求
用法:
image.png
除此之外,还可以用$.get()或者 .post()方法;
$.get('http://example.com',{name:'zhangsan',age:12},function(data){})
$.post('http://example.com',{name:'zhangsan',age:12},function(data){})
serialize方法:
作用:
将表单中的数据自动拼接成字符串类型的参数。
formData也提供了解决这个问题的方案,不过他是HTML5才支持的,老版本的浏览器并不适配。
var params = $('#form').serialize();
// name=zhangsan&age=20
还有一个方法:
serializeArray();
返回的是一个数组,数组的每一项都是一个对象
jquery的Ajax全局事件
.ajaxStart() // 当请求开始时触发
.ajaxComplete //当请求完成时触发
用法:
// 当页面中有ajax请求发送时触发,另一个类似
$(document).on('ajaxStart',function(){})
Nprogress插件用进度条,涓流动画来告诉用户真正发生的事情
http://ricostacruz.com/nprogress/
var btn = document.getElementById('btn')
btn.onclick = function(){
$.get('/index',function(data){console.log(data);
})
}
$(document).on('ajaxStart',function(){
NProgress.start()
console.log('start');
})
$(document).on('ajaxComplete',function(){
NProgress.done()
console.log('end');
})
//注:一开始写的是原生的ajax,$(document).on('ajaxStart',function(){})
//并没有进入回调函数,也就是说,并没有触发事件,但是可以成功得到服务器的响应。。。。
网友评论