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()//配置ajaxxhr.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//引入formidableconst 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 = resinsert.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()//进度条结束运动