Ajax初步大致写法
1.onreadystatechange 事件
当请求被发送到服务器时,我们需要执行一些基于响应的任务。每当 readyState 改变时,就会触发 onreadystatechange 事件。
readyState(状态值):存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
//xhr内部有5种状态,值分别为:0、1、2、3、4(了解即可)
xhr实例对象,在实例出来的那一刻状态就是0
xhr内部有5种状态,值分别为:0、1、2、3、4
0:实例出来的那一刻状态就是0,初始状态。
1:open已经调用了,但是send还没有调用,此时可以修改请求头内容。
2:send已经调用了,已经无法修改请求头
3:已经回来一部分数据了,小的数据会在此阶段一次性接收完毕,较大的数据有待进一步接收,响应头回来了。
4:数据全部接收完
status(状态码):响应的HTTP状态码。
//status,是指无论AJAX访问是否成功,由HTTP协议根据所提交的信息,服务器所返回的HTTP头信息代码。
HTTP状态码总共可分为5大类 :
1xx:信息响应类,表示接收到请求并且继续处理(例如;100客户必须继续发送请求)
2xx:处理成功响应类,表示动作被成功接收、理解和接受(例如;200交易成功)
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理(例如;300请求的资源可在多处得到)
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行(例如;404指所请求的页面不存在、已被删除或无法访问)
5xx:服务端错误,服务器不能正确执行一个正确的请求(例如;500服务器内部产生错误)总体理解:可以简单的理解为state代表一个整体的状态。而status是这个大的state下面具体的小的状态。
2.为什么onreadystatechange的函数实现要同时判断readyState和status呢?
只使用readyState
由于只使用readystate做判断,它不理会放回的结果是200、404还是500,只要响应成功返回了,就执行接下来的javascript代码,结果将造成各种不可预料的错误。所以只使用readyState判断是行不通的。
只使用status判断
事实上,结果却不像预期那样。响应码确实是返回了200,但是总共弹出了3次窗口!第一次是“readyState=2”的窗口,第二次是“readyState=3”的窗口,第三次是“readyState=4”的窗口。由此,可见onreadystatechange函数的执行不是只在readyState变为4的时候触发的,而是readyState(2、3、4)的每次变化都会触发,所以就出现了前面说的那种情况。可见,单独使用status判断也是行不通的。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>1_ajax小试牛刀</title> <style> #content{ width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> </head> <body> <h3>该页面是测试:ajax小试牛刀</h3> <button id="btn">点我发送请求(原生js-ajax-get)</button> <div id="content"></div> <script type="text/javascript" > //获取按钮 const btn = document.getElementById('btn') const content = document.getElementById('content') //给按钮绑定监听 btn.onclick = ()=>{ //1.创建xhr实例对象 const xhr = new XMLHttpRequest() //on 当xxx时候 //ready 准备 //state 状态 //change 状态 //xhr内部有5种状态,值分别为:0、1、2、3、4 //xhr实例对象,在实例出来的那一刻状态就是0 xhr.onreadystatechange = ()=>{ if(xhr.readyState === 4){ if (xhr.status >= 200 && xhr.status < 300) { console.log(xhr.response); content.innerHTML = `<h3>${xhr.response}</h3>` } } } //2.指定发送请求的:method、url xhr.open('GET','http://127.0.0.1:8080/test_get') //3.发送请求 xhr.send() } </script> </body> </html>
1.ajax中get请求(只能携带2种参数query和params)
1.形如:key=value&key=value 就是query参数的urlencoded编码形式
2.形如:/xx/xxx/老刘/18 就是params参数
<<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>3_ajax_get请求</title> <style> #content { width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> </head> <body> <h3>该页面是测试:ajax_get请求</h3> <button id="btn">点我发送请求(原生js-ajax-get)</button> <div id="content"></div> <script type="text/javascript"> //获取按钮 const btn = document.getElementById('btn') const content = document.getElementById('content') //给按钮绑定监听 btn.onclick = () => { //1.创建xhr实例对象 const xhr = new XMLHttpRequest() //绑定监听 xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { console.log(xhr.response); content.innerHTML = `<h3>${xhr.response}</h3>` } } } //#region /* 1.形如:key=value&key=value 就是query参数的urlencoded编码形式 2.形如:/xx/xxx/老刘/18 就是params参数 */ //#endregion //2.指定发送请求的:method、url、参数 // xhr.open('GET','http://127.0.0.1:8080/test_get?name=老刘&age=18') //携带query参数 xhr.open('PUT', 'http://127.0.0.1:8080/test_put') //携带params参数 //3.发送请求 xhr.send() } </script> </body> </html>
2. ajax中post请求【携带3种参数query,params和请求体(urlencoded,json)】
请求体比较常用:
1.若追加响应头用于标识携带请求体参数的编码形式--urlencoded
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
则
xhr.send('name=老刘&age=18') //携带urlencoded编码形式的请求体参数
2.若追加响应头用于标识携带请求体参数的编码形式--json
xhr.setRequestHeader('Content-type','application/json')
则
const person = {name:'老刘',age:20}
xhr.send(JSON.stringify(person)) //携带json编码形式的请求体参数
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>4_ajax_post请求</title> <style> #content{ width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> </head> <body> <h3>该页面是测试:ajax_post请求</h3> <button id="btn">点我发送请求(原生js-ajax-post)</button> <div id="content"></div> <script type="text/javascript" > //获取按钮 const btn = document.getElementById('btn') const content = document.getElementById('content') //给按钮绑定监听 btn.onclick = ()=>{ //1.创建xhr实例对象 const xhr = new XMLHttpRequest() //绑定监听 xhr.onreadystatechange = ()=>{ if(xhr.readyState === 4 ){ if(xhr.status >= 200 && xhr.status < 300){ console.log(xhr.response); content.innerHTML = `<h3>${xhr.response}</h3>` } } } //2.指定发送请求的:method、url、参数 xhr.open('POST','http://127.0.0.1:8080/test_post') //追加响应头用于标识携带请求体参数的编码形式--urlencoded xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded') //追加响应头用于标识携带请求体参数的编码形式--json //xhr.setRequestHeader('Content-type','application/json') //3.发送请求 const person = {name:'老刘',age:20} xhr.send('name=老刘&age=18') //携带urlencoded编码形式的请求体参数 // xhr.send(JSON.stringify(person)) //携带json编码形式的请求体参数 } </script> </body> </html>
3.解析json数据
responseType用于指定返回数据的格式
如果服务器传输的数据是其他类型(不为指定的返回类型),输出为null
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>5_ajax_解析json数据</title> <style> #content{ width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> </head> <body> <h3>该页面是测试:ajax_解析json数据</h3> <button id="btn">点我发送请求(原生js-ajax-get)</button> <div id="content"></div> <script type="text/javascript" > const btn = document.getElementById('btn') const content = document.getElementById('content') btn.onclick = ()=>{ //实例xhr const xhr = new XMLHttpRequest() //绑定监听 xhr.onreadystatechange = ()=>{ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status <300){ const {name,age,sex} = xhr.response//解构赋值 content.innerHTML = (` <ul> <li>姓名:${name}</li> <li>年龄:${age}</li> <li>性别:${sex}</li> <ul> `) } } } //配置请求 xhr.open('GET','http://127.0.0.1:8080/get_person') //responseType用于指定返回数据的格式 xhr.responseType = 'json' //发送请求 xhr.send() } </script> </body> </html>
4.连续解构赋值复习(打印c的值)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <script type="text/javascript"> let obj = {a: 1,b: {c: 2}} // const {c} = obj.b //标准的解构赋值 const {b: {c}} = obj //连续解构赋值 console.log(c) //2 const {b: {c: value}} = obj //连续解构赋值+重命名 console.log(value) //2 </script> </body> </html>
5.ajax使用步骤
ajax请求状态
6.Ajax请求异常和超时处理
1.配置出错的回调
xhr.onerror = ()=>{
alert('当前网络不稳定,请稍后重试');
}2.超时时间(等2秒数据回不来不要了)
xhr.timeout = 2000
3.超时的回调
xhr.ontimeout = ()=>{
alert('网速不给力,请切换网络重试');
}
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>7_ajax请求的异常与超时处理</title> <style> #content{ width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> </head> <body> <h3>该页面是测试:ajax请求的异常与超时处理</h3> <button id="btn">点我发送请求(原生js-ajax-get)</button> <div id="content"></div> <script type="text/javascript" > const btn = document.getElementById('btn') const content = document.getElementById('content') btn.onclick = function(){ //实例xhr const xhr = new XMLHttpRequest() //绑定监听 xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status <300){ const {name,age,sex} = xhr.response content.innerHTML = (` <ul> <li>姓名:${name}</li> <li>年龄:${age}</li> <li>性别:${sex}</li> <ul> `) } } } //配置请求 xhr.open('GET','http://127.0.0.1:8080/get_person_delay') //responseType用于指定返回数据的格式 xhr.responseType = 'json' //配置出错的回调 xhr.onerror = ()=>{ alert('当前网络不稳定,请稍后重试'); } //超时时间 xhr.timeout = 2000 //超时的回调 xhr.ontimeout = ()=>{ alert('网速不给力,请切换网络重试'); } //发送请求 xhr.send() } </script> </body> </html>
8.Ajax取消(关闭)请求
xhr.abort()
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>8_ajax取消请求</title> <style> #content{ width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> </head> <body> <h3>该页面是测试:ajax取消请求</h3> <button id="btn">点我发送请求(原生js-ajax-get)</button> <button id="btn2">取消请求</button> <div id="content"></div> <script type="text/javascript" > const btn = document.getElementById('btn') const btn2 = document.getElementById('btn2') const content = document.getElementById('content') let xhr btn.onclick = ()=>{ //实例xhr xhr = new XMLHttpRequest() //绑定监听 xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status <300){ const {name,age,sex} = xhr.response content.innerHTML = (` <ul> <li>姓名:${name}</li> <li>年龄:${age}</li> <li>性别:${sex}</li> <ul> `) } } } //配置请求 xhr.open('GET','http://127.0.0.1:8080/get_person_delay') //responseType用于指定返回数据的格式 xhr.responseType = 'json' //配置出错的回调 xhr.onerror = ()=>{ alert('当前网络不稳定,请稍后重试'); } //超时时间 xhr.timeout = 2000 //超时的回调 xhr.ontimeout = ()=>{ alert('网速不给力,请切换网络重试'); } //发送请求 xhr.send() } btn2.onclick = ()=>{ xhr.abort() } </script> </body> </html>
9.Ajax避免多次请求(网络不好时,用户多次点击)
外部定义一个变量isLoading ,点击事件用if一个判断是否为以展示去取消abort(),监听事件中改为false,发送请求后改为true
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>9_避免多次重复请求</title> <style> #content{ width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> </head> <body> <h3>该页面是测试:避免多次重复请求</h3> <button id="btn">点我发送请求(原生js-ajax-get)</button> <div id="content"></div> <script type="text/javascript" > const btn = document.getElementById('btn') const content = document.getElementById('content') let xhr let isLoading btn.onclick = ()=>{ if(isLoading) xhr.abort() //实例xhr xhr = new XMLHttpRequest() //绑定监听 xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status <300){ isLoading = false const {name,age,sex} = xhr.response content.innerHTML = (` <ul> <li>姓名:${name}</li> <li>年龄:${age}</li> <li>性别:${sex}</li> <ul> `) } } } //配置请求 xhr.open('GET','http://127.0.0.1:8080/get_person_delay') //responseType用于指定返回数据的格式 xhr.responseType = 'json' //发送请求 xhr.send() isLoading = true } </script> </body> </html>
10.jquery封装的ajax
语法:$.ajax({ 本次发送 ajax 的配置项 })
配置项:
1.url:必填,表示请求地址
2.method:选填,默认是 GET ,表示请求方式
3.data:选填,默认是 ’ ’ ,表示携带给后端的参数
5.dataType:选填,默认自动识别,表示后端返回给你的数据类型
6.async:选填,默认是 true,表示是否异步
7.success:选填,表示请求成功的回调函数(可以传3个参数,第一个结果result,第二 个是responsetext表示成功还是失败,第三个是xhr)
8.error:选填,表示请求失败的回调函数
精简版没有失败回调<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>10_jQuery封装的ajax</title> <style> #content{ width: 300px; height: 100px; border: 1px solid black; margin-top: 10px; } </style> <script type="text/javascript" src="./js/jquery.min.js"></script> </head> <body> <h3>该页面是测试:jQuery封装的ajax</h3> <button id="btn1">点我发送请求(jQuery-ajax-get)</button> <button id="btn2">点我发送请求(jQuery-ajax-post)</button> <div id="content"></div> <script type="text/javascript" > const btn1 = $('#btn1') const btn2 = $('#btn2') const content = $('#content') btn1.click(()=>{ //使用jQuery发送ajax-get(完整版) $.ajax({ url:'http://127.0.0.1:8080/test_jquery_get', //请求地址 method:'GET',//请求方式(默认值是GET) data:{school:'atguigu'},//携带的数据 dataType:'json',//配置响应数据格式 timeout:2000,//指定超时的时间 success:(result,reponseText,xhr)=>{ console.log(result,reponseText,xhr); content.append(`<div>汽车名:${result.name},价格:${result.price}</div>`) },//成功的回调 error:(xhr)=>{console.log('请求出错了',xhr);} //失败的回调 }) //使用jQuery发送ajax-get(精简版) /* $.get('http://127.0.0.1:8080/test_jquery_get',{school:'atguigu'},(data)=>{ console.log(data); content.append(`<div>汽车名:${data.name},价格:${data.price}</div>`) },'json') */ }) btn2.click(()=>{ //使用jQuery发送ajax-post(完整版) $.ajax({ url:'http://127.0.0.1:8080/test_jquery_post', //请求地址 method:'POST',//请求方式(默认值是GET) data:{school:'atguigu'},//携带的数据 dataType:'json',//配置响应数据格式 timeout:2000,//指定超时的时间 success:(result,reponseText,xhr)=>{ console.log(result,reponseText,xhr); content.append(`<div>汽车名:${result.name},价格:${result.price}</div>`) },//成功的回调 error:(xhr)=>{console.log('请求出错了',xhr);} //失败的回调 }) //使用jQuery发送ajax-post(精简版) $.post('http://127.0.0.1:8080/test_jquery_post',{school:'atguigu'},(data)=>{ console.log(data); content.append(`<div>汽车名:${data.name},价格:${data.price}</div>`) },'json') }) </script> </body> </html>
11.jsonp解决跨域(工作几乎不用,面试常问)
1.跨域(违背了同源策略:协议名,主机名,端口号不一致)
2.非同源受到哪些限制
cookie不能读取
DOM无法获得
Ajax不能获取数据
前端定义函数函数,后端调用函数
原理:绕开了xhr,借助了<script>标签发请求不受同源策略的限制,把数据拿回来
通常用jsonp后端就用callback这个命名来用
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <h3>当前页面一定不要用服务器去打开,因为要制造跨域问题,用jsonp去解决</h3> <button id="btn">点我获取数据</button> <script type="text/javascript" > const btn = document.getElementById('btn') btn.onclick = ()=>{ //1.创建script节点 const scriptNode = document.createElement('script') //2.给节点指定src属性(请求地址) scriptNode.src = 'http://localhost:8080/test_jsonp?callback=peiqi' //3.将节点放入页面 document.body.appendChild(scriptNode) //4.准备好一个函数 window.peiqi = (a)=>{ console.log(a); } //5.移除已经使用过的script节点 document.body.removeChild(scriptNode) } </script> </body> </html>
服务器
//引入express const express = require('express') const cors = require('cors') //创建app实例对象 const app = express() //使用中间件解析urlencoded编码形式的请求体参数 app.use(express.urlencoded({extended:true})) //使用中间件解析json编码形式的请求体参数 app.use(express.json()) app.use(cors()) //暴露静态资源 app.use(express.static(__dirname+'/src')) app.get('/test_jsonp',(request,response)=>{ const {callback} = request.query//接收传入的数据window中的名字peiqi console.log(callback); const person = [{name:'tom',age:18},{name:'老刘',age:5}] response.send(`${callback}(${JSON.stringify(person)})`) })
面试:
1.json和jsonp有关系吗?
有一定关系,但是这两个是两回事。json是存储和交互数据的格式,jsonp是后端解决跨域的方式,为什么说他两有关系呢是因为后端返回数据必须打成JOSN字符串。例如:${JSON.stringify(person)
2.用jsonp解决跨域也是用到xhr是吗?
完全没有用到
3.用jsonp解决跨域有哪些缺点?
1.后端必须配合前端把数据转换成JOSN格式(${JSON.stringify(person))
2..后端必须配合前端写成函数调用形式${callback}()
12. jquery封装的jsonp
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Document</title> <script type="text/javascript" src="./js/jquery.min.js"></script> </head> <body> <h3>当前页面一定不要用服务器去打开,因为要制造跨域问题,jquery封装的jsonp</h3> <button id="btn">点我获取数据</button> <script type="text/javascript" > const btn = $('#btn') btn.click(()=>{ $.getJSON('http://localhost:8080/test_jsonp?callback=?',{},(data)=>{ console.log(data); }) }) </script> </body> </html>
13.cors解决跨域
1.前端人员无需进行特殊操作
2.用Access解决跨域不是一个两个响应头就能解决,需要一组响应头解决
列如: response.setHeader('Access-Control-Allow-Origin','*') 【面试时问跨域问题的响应头建议写这个】
response.setHeader('Access-Control-Expose-Headers','*')
response.setHeader('Access-Control-Allow-Methods','*')
3.put delete这两个请求都属于复杂请求,这些请求在真正请求之前都需要嗅探请求(也 可以叫做预请求)
安装yarn add cors中间件,用与在espress解决跨域问题,
app.use(cors())使用中间件,全局都能使用了,可以随便请求了
服务器
//引入express const express = require('express') const cors = require('cors') //创建app实例对象 const app = express() //使用中间件解析urlencoded编码形式的请求体参数 app.use(express.urlencoded({extended:true})) //使用中间件解析json编码形式的请求体参数 app.use(express.json()) app.use(cors())//全局都使用响应头 //暴露静态资源 app.use(express.static(__dirname+'/src')) //响应GET请求--可以接收query参数 app.get('/test_get',(request,response)=>{ console.log('有人请求test_get了--携带的query参数是:',request.query); /* response.setHeader('Access-Control-Allow-Origin','*')//*代表全部路径,也可以替换为具体的路径 response.setHeader('Access-Control-Expose-Headers','*') */ response.send('hello_test_get') }) /* app.options('/test_put',(request,response)=>{ response.setHeader('Access-Control-Allow-Origin','*') response.setHeader('Access-Control-Expose-Headers','*') response.setHeader('Access-Control-Allow-Methods','*') response.send() }) */ app.put('/test_put',(request,response)=>{ /* response.setHeader('Access-Control-Allow-Origin','*') response.setHeader('Access-Control-Expose-Headers','*') */ response.send('hello_test_put') })
测试cors解决跨域
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>测试cors解决跨域</title> </head> <body> <h3>当前页面一定不要用服务器去打开,因为要制造跨域问题,测试cors解决跨域</h3> <button id="btn">点我获取数据</button> <script type="text/javascript" > const btn = document.getElementById('btn') btn.onclick = ()=>{ const xhr = new XMLHttpRequest() xhr.onreadystatechange = ()=>{ if(xhr.readyState === 4){ if(xhr.status === 200){ console.log(xhr.response); console.log(xhr.getAllResponseHeaders()); } } } xhr.open('PUT','http://localhost:8080/test_put') xhr.send() } </script> </body> </html>