Ajax
Ajax概述
它是浏览器提供的一套方法,可以实现页面无刷新更新数据
-
应用场景
- 页面上拉加载更多数据
- 列表数据无刷新分页
- 表单项离开焦点数据验证
- 搜索框提示文字下拉列表
-
Ajax的运行环境
Ajax技术需要运行在网站环境汇总才能生效,
Ajax的实现步骤
-
创建Ajax对象
var xhr = new XMLHttpRequest()
-
告诉Ajax请求地址以及请求方式
//第一个参数为请求方式,第二个参数是请求地址 xhr.open('get', 'http://localhost:8000')
-
发送请求
xhr.send()
-
获取服务器端给客户端的响应数据
监听xhr的onload
事件xhr.onload = function() { //xhr.responseText是响应给服务器端的数据 console.log(xhr.responseText) }
服务器端响应的数据格式
在项目中,服务器端大多数情况下会以JSON对象作为响应数据的格式,当客户端拿到响应数据时,要将JSON数据和HTML字符串进行拼接,然后将拼接的结果展示在页面中
在http请求与响应的过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会被转换成对象字符串进行传输
JSON.parse()//将JSON字符串转换为JSON对象
请求参数传递
- GET请求
//请求参数需要手动拼接字符串
xhr.open('get', 'http://localhost:8000/get?name=zhangsan&age=18')
```
- POST请求
```js
//设置请求参数格式类型
//setRequestHeader第一个参数是报文属性名称,第二个参数是报文属性对应的值
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencode')
xhr.send('name=zhangsan&age=20')
请求参数格式
-
application/x-www-form-urlencode
name=zhangsan&age=18&sex=male
-
application/json
{name: 'zhangsan', age: 18, sex: 'male'}
在请求头中指定Content-Type属性是application/json,告诉服务器端当前请求参数的格式是json
JSON.stringify() //将json对象转换为json字符串的方法
注意:get请求是不能提交json对象数据格式的,传统网站的表单提交也是不支持json对象数据格式的
Ajax运行原理及实现
获取服务器端的响应
-
ajax状态码
在创建ajax对象,配置ajax对象,发送请求,以及接受完服务器端响应数据,这个过程中的每一步都会对应一个数值,这个数值就是ajax状态码0:当xhr被实例化出来,状态就是0,。即初始化状态
1:请求已经建立但是还没有发送,即:send方法还没有被调用,依然可以修改请求头
2: 请求已经发出去了,即:send方法已经被调用了,不能再修改请求头,响应首行和响应头已经回来了
3:数据回来了(但是数据可能不完整,如果数据小,会在此阶段直接接受完毕,数据大有待进一步接收)
4:响应已经完成,数据完全回来了,可以获取并使用xhr.readyState //获取ajax状态码
onreadystatechange//事件监听状态码变化
ajax错误处理
- 网络畅通,服务器端能接受到请求,服务器端返回的救国不是预期结果
可以判断服务器端返回的状态码,分别进行处理。
xhr.status
获取状态码 - 网络畅通,服务器没有接受到请求,返回404状态码
检查请求地址是否错误 - 网络畅通服务器端能接受到请求,服务器端返回500状态码
服务端错误,后端的锅
//当网络中断时会触发onerror事件
xhr.onerror = function(){
alert('网络中断')
}
-
低版本ie的缓存问题
在低版本ie中,ajax有严重的缓存问题,即在请求地址不发生变化的情况下,只有第一次请求会真正发送到服务端,后续的请求都会从浏览器的缓存中获取结果,几十服务器端的数据跟新了,客户端依然拿到的是缓存中的旧数据
解决方案:在请求地址的后面加请求参数,保证每一次请求中的请求参数值不相同
xhr.open('get'.'localhost:8000/index?t=' + Date.now())
Ajax异步编程
-
概述
同步:
-
上一行代码执行完之后,才能执行下一行代码,代码逐行执行
console.log('before'); console.log('after');
异步:
-
异步代码虽然要花时间去执行,但程序不会等到异步代码执行完后再执行后面的代码,而是直接执行后面的代码,当后续代码执行完后,再回头看异步代码是否返回结果,如果已有返回结果,再调用事先准备好的回调函数处理异步代码的执行结果
console.log('before') setTimeout(()=>{ consol.log('last') }) console.log('after')
-
ajax封装
**问题:**发送一次请求代码过多,发送多次请求代码冗余且重复
**解决方案:**将请求代码封装到函数中,发送请求时调用函数
function ajax({type, url, data, header, success, error}){
const xhr = new XMLHttpRequest();
//定义一个拼接参数的变量
let params = '';
for(let item in data) {
//将参数转换成字符串格式
params += item + '=' + data[item] + '&'
}
//去掉参数最后面的&符
params = params.substr(0, params.length - 1)
//如果请求方式为get,将请求参数拼接在url后面
if(type == 'get') {
url = url + '?' + params;
}
//配置ajax对象
xhr.open(type, url);
//如果请求参数为post,设置请求参数格式类型,并在发送请求时将参数传递进去
if(type == 'post') {
//用户希望向服务器端传递请求参数的类型
const contentType = header['Content-Type'];
xhr.setRequestHeader('Content-Type', header['Content-Type']);
//判断用户设置的请求参数类型
if(contentType == 'application/json') {
//如果是json格式,直接传递参数json字符串
xhr.send(JSON.stringify(data))
} else {
//发送请求
xhr.send(params);
}
} else {
//发送请求
xhr.send();
}
//监听xhr对象下的onload事件,当xhr对象接收完响应数据后触发
xhr.onload = function(){
//获取响应头中的数据
const resContentType = xhr.getResponseHeader('Content-Type') //参数为要获取的类型
//服务端返回的数据
let response = xhr.responseText
//判断返回的数据是否是JSON字符串
if(resContentType.includes('application/json')) {
response = JSON.parse(response)
}
//判断http状态码,等于200时调用处理成功情况的函数
if(xhr.status == 200) {
success(response, xhr);
} else {
//否则调用请求失败的函数
error(response, xhr)
}
}
}
模板引擎
-
下载art-template模板引擎并在HTML页面中引入库文件
<script src="./js/template-web.js"></script>
-
准备art-template模板
<script id="tpl" type="text/html"> <div class="box"></div> </script>
-
告诉模板引擎将哪一个模板和哪个数据进行拼接
let html = template('tpl',{username: 'zhangsan', age: 20})
-
将拼接好的html字符串添加到页面中
document.getElementById('container').innerHTML = html
FormData
-
作用
- 模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数格式
- 异步上传二进制文件
-
使用
<!-- html中 --> <form action="" id="form"> <input type="text" name="username"> <input type="password" name="password" id=""> <input type="button" id="btn" value="提交"> </form> <script> //获取按钮 const btn = document.querySelector('#btn') //获取表单 const form = document.querySelector('#form') btn.addEventListener('click',function(){ //将普通的html表单转换成表单对象 const formData = new FormData(form) //创建ajax对象 const xhr = new XMLHttpRequest() //配置ajax xhr.open('post', 'http://localhost:8000/form') //发送ajax请求 xhr.send(formData) //监听xhr的onload事件 xhr.onload = function(){ //判断状态码 if(xhr.status == 200) { console.log(xhr.responseText) } } }) </script>
//app.js //引入formidable const formidable = require('formidable') app.post('/form', (req, res)=> { //创建表单解析对象 const form = new formidable.IncomingForm() form.parse(req,(err, fields , files)=> { res.send(fields) }) })
formData对象的实例方法
-
获取表单对象中的属性值
formData.get('key') //key是表单name属性的值
-
设置表单对象中属性的值
如果设置的表单属性存在,将替换原有的值
如果不存在,将会创建这个表单属性formData.set('key', 'value') //key是表单name属性的值,value为将要设置的值
-
删除表单对象中的属性
formData.delete('key')
-
向表单对象中追加属性值
formData.append('key', 'value')
**注意:**set方法与append方法的区别是,在 属性名已存在的情况下,set会覆盖已有键名的值,append会保留两个值
formData二进制文件上传
<input type="file" id="file"/>
const file = document.querySelector('#file')
//监听表单change事件(用户选择文件的时候)
file.onchange = function(){
//创建空表单对象
let formData = new FormData()
//将用户选择的文件追加到formData对象中
formData.append('attrName', this.files[[0]])
//创建ajax对象
let xhr = new XMLHttpRequest()
//对ajax对象进行配置
xhr.open('post', 'http://localhost/8000/upload')
//发送ajax请求
xhr.send(formData)
//监听服务端响应给客户端的数据
xhr.onload = function(){
//判断状态码
if(xhr.status == 200) {
}
}
}
-
文件上传进度展示
监听xhr对象下的upload下的progress事件,事件对象
e
中的loaded代表已上传的文件大小,total代表文件总大小<style> #out { width: 800px; height:20px; background-color: rgba(245,245,245,.9); text-align: center; color: red; } #insert { width: 0%; height:100%; background: aquamarine; } </style> <form action=""> <input type="file" name="attrName" id="file"> </form> <div id="out"> <div id="insert">0%</div> </div>
const file = document.querySelector('#file') const insert = document.querySelector('#insert') //监听表单change事件(用户选择文件的时候) file.onchange = function(){ //创建空表单对象 let formData = new FormData() //将用户选择的文件追加到formData对象中 formData.append('attrName', this.files[[0]]) //创建ajax对象 let xhr = new XMLHttpRequest() //对ajax对象进行配置 xhr.open('post', 'http://localhost:8000/upload') //监听上传进度信息 xhr.upload.onprogress = function(e){ let res = parseInt((e.loaded / e.total) * 100) + '%' console.log(res); insert.style.width = res insert.innerHTML = res } //发送ajax请求 xhr.send(formData) //监听服务端响应给客户端的数据 xhr.onload = function(){ //判断状态码 if(xhr.status == 200) {} } }
Ajax请求限制
- cookie无法读取
- DOM无法获取
- Ajax请求可以发送,但响应的数据被拦截
- 同源策略
如果连个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源
JSONP解决跨域
JSONP(json with padding),是一个非官方的跨域解决方案,只支持get请求,利用的是标签请求资源不受同源策略限制的特点
步骤:
<button id="btn">点击发送请求获取数据</button>
<script>
//获取需要监听事件的元素
let btn = document.querySelector('#btn')
//监听事件
btn.addEventListener('click', function(){
// 1.动态创建script标签
let scriptNode = decument.createElement('script')
//2.定义一个处理数据的函数(必须是全局作用域下的函数)
window.getData = function(data){
console.log(data)
}
//3.给动态创建的script标签加上请求地址(callback是要使用数据的函数的名)
scriptNode.src = 'http://localhost:8000/getData?callback=getData'
//4.将标签放入页面,请求数据
document.body.appendChild(scriptNode)
})
</script>
关于jsonp解决跨域的说明:
- 原理:利用了标签发送GET请求“天然跨域”(不受同源策略的限制)
- 套路:
- 创建script节点,指定src
- 定义好一个数据处理函数
- 把数据处理函数的名称传递给后端
- 后端返回符合js函数调用语法的字符串
- 局限性:
- 只能解决GET请求跨域问题
- 必须需要后端配合(配合返回调用函数的字符串)
cors解决跨域(服务端允许跨域请求)
app.use((req, res, next)=> {
//第二个参数为 * 则允许所有网站跨域请求
res.header('Access-Control-Allow-origin', 'http://localhost:3000')
//第二个参数为允许的跨域请求方式
res.header('Access-Control-Allow-Methods', 'get, post')
next()
})
withCredentials属性
在使用ajax技术发送跨域请求时,默认情况不会在请求中携带cookie信息(出于对安全性的考虑)
**xhr下的属性:**withCredentials:指定在涉及到跨域请求时们是否携带cookie信息,默认值为false
xhr.withCredentials = true
**服务端响应头:**Access-Countrol-Allow-Credentials:true允许允许发送请求时客户端携带cookie
res.header('Access-Control-Allow-Credentials', true)
JQuery中的$.ajax()
$.ajax({
type: 'get',
//如果协议、域名、端口,都相同的情况下,不必写完整的请求地址
url: 'http://localhost:3000',
//也可以传递字符串参数值,'name=zhangsan&age=18'
data: {name: 'zhangsan', age: '20'},
//如果需要传递JSON格式,将下面设置为'application/json'
contentType: 'application/x-www-form-urlencoded',
//请求发送之前需要做的事,例如表单格式验证,如果不符将return false,阻止发送请求
beforeSend: function(){return false},
//请求成功被调用
//返回的数据:会根据服务端返回的数据类型,自动转换为相应的类型
success: function(response){},
//请求失败被调用
//接收ajax对象做参数,处理错误信息
error: function(xhr){}
})
serialize()方法
将表单内容拼接成字符串类型的参数
$('#form').serialize()
//username=zhangsan&age=20
封装将表单内容转换成对象的方法
function(obj){
let result = {};
//将表单数据转换成数组,例:[{name: 'username', value: 'zhangsan'}, {name: age, value: '20'}]
let params = obj.serializeArray();
//循环这个数组将数组转换成对象
$.each(params, function(index, value){
result[value.name] = value.value;
})
return result
}
发送jsonp请求
$.ajax({
url: 'http://localhost:3000',
//代表发送的是jsonp请求
dataType: 'jsonp',
//可选参数:修改向服务器传递函数名的参数的名称(默认是callback)
jsonp: 'cb',
//可选参数:指定函数名称(一般用不上)
jsonCallback: 'fnName',
success: function(response){}
})
$.get()
和$.post()
//第二个向服务器传递参数的参数为可选参数,可以是字符串或对象
$.get('http://localhost:3000', {name: 'zhangsan'}, function(response){})
$.post('http://localhost:3000', {name: 'zhangsan'}, function(response){})
ajax全局事件
只要页面中有Ajax请求被发送,对应的全局事件就会背触发
//当请求开始发送时触发
$(document).on('ajaxStart',function(){})()
//当请求完成时触发
$(document).on('ajaxComplete',function(){})()
NProgress进度条插件
官宣:纳米级进度条,使用逼真的涓流动画来告诉用户正在发生的事
<!--引入相应的js与css文件-->
<link rel="stylesheet" href="nprogress.css">
<script src="nprogress.js"></script>
Nprogress.start()//进度条开始运动
NProgress.done()//进度条结束运动