一,准备知识
1、Ajax优缺点
优点:
-
可以不刷新页面与服务器通信
-
可以根据用户事件更新部分页面内容
缺点:
-
无浏览记录,无法回退
-
存在跨域问题
-
SEO不友好
2、HTTP协议
超文本传输协议,规定了浏览器与万维网服务器之间的交换规则,主要约定了两块内容:请求和响应。
①.请求报文(重点:记忆格式与参数就ok了)
四部分:
请求行(包含三部分:请求类型、URL路径、HTTP协议版本);
请求头;
空行;
请求体;
行: POST /s?ie=utf-8 HTTP/1.1
头: Host: baidu.com
Cookies: name = wd
Content-type: application/x-www-form-urlencoded
User-Agent: Chrome 83
格式:名字:空格 值
空行
请求体:get请求为空 post请求可以不为空
比如:username = admin&password = admin
②.响应报文
行:HTTP/1.1 200 OK (协议版本 状态码 状态字符串
头 类型 长度
空行
体 返回结果
3、浏览器控制台查看报文
-
F12
-
Network
-
刷新-会列出来网页发送的所有的请求
-
点第一个请求
-
会出现Headers Preview Respoonse(响应体在这看) Initiator Timing Cookies
重点是 Headers(重点Response Headers:响应头 Request Headers:请求头) Response
请求报文查看:Headers下面的请求头Request Header +请求体 Form Data 响应报文查看 Headers下面的Response Headers和Response
-
使用get请求有参数的时候 在Headers里面还会有一个Query String Parameters放参数的地方;
4、安装Node.js Express框架
why:Ajax会给服务端发请求,需要一个服务端,而Express是基于Node的一个服务端框架,只需要掌握基本使用就ok了
安装完Node.js(node安装省略,很简单),后安装express:::
-
在vscode软件项目最外层文件中,打开终端
-
终端输入代码初始化:npm init --yes
-
安装express框架:npm i express
-
引入express
//1. 引入express const express = require('express'); //2. 创建应用对象 const app = express(); //3. 创建路由规则 // request 是对请求报文的封装 // response 是对响应报文的封装 app.get('/', (request, response)=>{ //设置响应 response.send('HELLO EXPRESS'); }); //4. 监听端口启动服务 app.listen(8000, ()=>{ console.log("服务已经启动, 8000 端口监听中...."); });
5.执行脚本:node express基本使用.js
6.浏览器查看http://127.0.0.1:8000/,输出HELLO EXPRESS
二、Ajax基本操作
1、点击获取数据的案例(GET)
案例:HTML页面有一个div框 有一个button框 点击按钮 使接收到的请求数据返回到div框内
①准备工作:
准备:1、HTML页面文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AJAX GET 请求</title> <style> #result{ width:200px; height:100px; border:solid 1px #90b; } </style> </head> <body> <button id = "btn">点击发送请求</button> <div id="result"></div> <script> //获取button元素 const btn = document.querySelector('#btn'); const result = document.querySelector('#result') </script> </body> </html>
2、express.js文件
//1. 引入express const express = require('express'); //2. 创建应用对象 const app = express(); //3. 创建路由规则 // request 是对请求报文的封装 // response 是对响应报文的封装 app.get('/server', (request, response)=>{ //设置响应头 response.setHeader('Access-Control-Allow-Origin','*'); //设置响应体 response.send('HELLO AJAX'); }); //4. 监听端口启动服务 app.listen(8000, ()=>{ console.log("服务已经启动, 8000 端口监听中...."); });
特别注意:
路由设置./server是指浏览器向服务器发送请求的时候,ulr路径也就是请求行的第二行内容,路径是/server,则会执行后面的回调函数,由response执行响应
响应头的设置response.setHeader('Access-Control-Allow-Origin','*'); 代表设置可以跨域
②创建一个Ajax对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AJAX GET 请求</title> <style> #result{ width:200px; height:100px; border:solid 1px #90b; } </style> </head> <body> <button id="btn">点击发送请求</button> <div id="result"></div> <script> //获取button元素 const btn = document.querySelector('#btn'); const result = document.querySelector('#result') //绑定事件写法 btn.addEventListener('click',function(){ //1. 创建对象 const xhr = new XMLHttpRequest(); //2. 初始化 设置请求方法和 url xhr.open('GET', 'http://127.0.0.1:8000/server'); //3. 发送 xhr.send(); //4. 事件绑定 处理服务端返回的结果 // on when 当....时候 // readystate 是 xhr 对象中的属性, 表示状态 0 1 2 3 4 // change 改变 xhr.onreadystatechange = function(){ //判断 (服务端返回了所有的结果) if(xhr.readyState === 4){ //判断响应状态码 200 404 403 401 500 // 2xx 成功 if(xhr.status >= 200 && xhr.status < 300){ //处理结果 行 头 空行 体 //响应 // console.log(xhr.status);//状态码 // console.log(xhr.statusText);//状态字符串 // console.log(xhr.getAllResponseHeaders()); //所有响应头 // console.log(xhr.response);//响应体 //设置 result 的文本 result.innerHTML = xhr.response; }else{ } } } }) </script> </body> </html>
③如何给Ajax请求中设置url参数
原始代码见上面代码块28 29行
//原始代码 xhr.open('GET', 'http://127.0.0.1:8000/server'); //如何传参 //直接在设置url后面+参数,问号分割,参数名字=值,然后&连接下一个参数和值 xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=200&c=300'); //如何在Chrome调试查看参数-F12-Network-Headers-Query String Parameters-可以查看参数是否存在
2、AJAX的POST请求案例
页面中一个div框,鼠标放在框内的时候,发送一个POST请求,将响应体在div内部进行呈现
①准备工作:
HTML方面:设置div框 绑定元素 绑定事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AJAX POST 请求</title> <style> #result{ width:200px; height:100px; border:solid 1px #903; } </style> </head> <body> <div id="result"></div> <script> //获取元素对象 const result = document.getElementById("result"); //绑定事件 result.addEventListener("mouseover", function(){ //后面写 }); </script> </body> </html>
JS方面:
路由规则 方法改为post(小写)
app.post('/server', (request, response) => { //设置响应头 设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*'); //设置响应体 response.send('HELLO AJAX POST'); });
②创建AJAX对象
下面的代码放在上面的function中
//1. 创建对象 const xhr = new XMLHttpRequest(); //2. 初始化 设置类型与 URL xhr.open('POST', 'http://127.0.0.1:8000/server'); //设置请求头,open后面这个方法是设置请求头,接受两个参数,一个是头的名字,第二个是值,后面会讲,跟本小节案例无关 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); //3. 发送 xhr.send(); //4. 事件绑定 xhr.onreadystatechange = function(){ //判断 if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300){ //处理服务端返回的结果 result.innerHTML = xhr.response; } } }
③POST请求中如何设置参数
在页面文件xhr.send()的send方面中设置参数,见上面代码块的第8行
xhr.send('a=100&b=200&c=300'); //请求体的这些参数 可以写任意格式的参数和值,如 //xhr.send('a:100&b:200'); //xhr.send('12345')
3、AJAX如何设置请求头信息
见上面代码块5-6行
//HTML文件中的操作 //设置自带的头信息属性 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); //加入自定义的头信息,比如以下 xhr.setRequestHeader('name','ycc'); //上面这行代码在浏览器中运行会报错,浏览器有安全机制,name属性是我们单独加的自定义属性,因此会报错。如果需要解决报错这个问题,需要在后端文件中,设置响应头的地方加上下面这行代码,而在实际开发中,这种事一般是后端人员的工作 //JS文件中的操作 //下面这行代码表示所有类型的头信息浏览器都可以接收 response.setHeader('Access-Control-Allow-Headers','*') //还需要把js文件里的路由规则的app.post改成app.all
4、服务端响应JSON数据
why:在开发中 大部分时候都是返回的JSON格式的数据,因此这块必须掌握,知道结果怎么处理
案例:HTML中有一个div框,键盘按下任意按键,就会对服务器发送请求,将响应数据中的值在div中呈现
HTML部分
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #box{ height: 200px; width: 300px; border: 1px solid red; } </style> </head> <body> <div id="box"></div> <script> const box = document.querySelector('#box'); window.addEventListener('keydown',()=>{ //1.创建Ajax实例 const xhr = new XMLHttpRequest(); //2.设置响应体数据的类型,接收到的数据类型为json 后面方便直接调用,和下面的方法一不能并存 xhr.responseType = 'json'; //3.初始化,发送请求,获取路径 xhr.open('GET','http://127.0.0.1:8000/json-server'); //4.发送send请求 xhr.send(); //5.事件绑定,当服务器响应结束后应该进行的操作 xhr.onreadystatechange = function(){ //判断是否服务器到第四个阶段 if(xhr.readyState === 4){ //判断是否状态码为2**,成功状态码 if(xhr.status >= 200 & xhr.status <300){ //进行json数据操作,得到服务器响应数据 //测试是否得到响应体 // console.log(xhr.response); // box.innerHTML = xhr.response; // /*方法一:手动数据转换,xhr.response是一个json格式字符串 // 想要获取其中的一个数据就比较麻烦,可以借助下面的方法转换成一个对象*/ // let data = JSON.parse(xhr.response); // // console.log(data); // box.innerHTML = data.age; // 方法二:自动转换数据,23行取消注释,设置接收数据类型为json格式,自动转换成对象 box.innerHTML = xhr.response.age; } } } }) </script> </body> </html>
易错点:①:25行http://127.0.0.1:8000/json-server中的//不能少 ②23行的代码跟41行的Json.parse不能一起使用
js部分
//一、JSON需要的js文件 //1.引入express const express = require('express'); //2.创建应用 const app = express(); //3.创建理由规则-Json响应 app.all('/json-server',(request,response) => { //设置允许跨域的响应头 response.setHeader('Access-Control-Allow-Origin','*'); //设置允许自定义响应头 response.setHeader('Access-Control-Allow-Headers','*'); //响应一个数据 const data ={ age: 26 }; //对对象进行字符串转化,因为最后的send方法只能接受字符串类型 let str = JSON.stringify(data); //设置响应体,发送数据 response.send(str); }); //监听端口启动服务 app.listen(8000, ()=>{ console.log("服务已经启动, 8000 端口监听中...."); });
5、下载nodemon插件
why:AJAX练习学习,会经常修改服务端代码,nodemon插件可以帮助在服务端代码改变的时候,自动重启
直接在vscode终端进行操作安装:npm install -g nodemon
使用方法:nodemon server.js启动软件
6、IE缓存问题
IE浏览器和AJAX交互的时候,会把交互的结果进行缓存,下一次再触发事件的时候,并不会重新使用Ajax发送请求,而是直接从本地缓存调取数据,因此,需要解决这个问题,一般而言就是在初始化发送请求的URL后面加一个参数
//原始代码 xhr.open('Get','http://127.0.0.1:8000/ie') //修改之后的代码 xhr.open('Get','http://127.0.0.1:8000/ie?t='Date.now())
7、请求超时与网络异常响应
主要依靠在js文件中使用 SetTimeOut
主要代码:
1、超时设置,超过多少毫秒就算超时,取消发送请求
xhr.timeout = 2000 ;
2、超时回调,请求超时后执行后面的回调函数
xhr.ontimeout = function(){
//执行操作,比如弹窗提醒 }
3、网络异常回调(如何测试,浏览器F12-Network-Online)
xhr.onerror = function(){}
案例:按钮+div,①点击按钮,如果超时就取消发送请求并执行回调弹窗提醒 ②测试网络异常情况下,弹窗提醒
准备:因为要测试延迟,我们可以以2秒钟算超时,那么需要再服务端js中加一个定时器,比如三秒钟发送响应报文
开干:
HTML:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>延时响应测试</title> <style> #box{ border: 1px solid red; width: 300px; height: 200px; } </style> </head> <body> <button id="btn">点击发送数据</button> <div id="box"></div> <script> //绑定点击事件 const btn = document.querySelector('#btn'); const box = document.getElementById('box'); btn.addEventListener('click',()=>{ //创建AJAX实例 const xhr = new XMLHttpRequest(); //初始化,设置获取方法和路径 xhr.open('GET','http://127.0.0.1:8000/delay'); //发送请求 xhr.send(); //超时设置,超过多少毫秒就算超时,取消发送请求 xhr.timeout = 2000; //超时回调,请求超时后执行后面的回调函数 xhr.ontimeout = function(){ alert('请求超时'); } //网络异常回调 xhr.onerror = function(){ alert('网络异常') } //绑定服务器事件 xhr.onreadystatechange = function(){ //判断是否到服务器第四个阶段 if(xhr.readyState === 4){ //判断是否返回2**成功状态码 if(xhr.status >= 200 & xhr.status < 300){ //进行操作响应,根据服务端的响应,延迟三秒后出现 box.innerHTML = xhr.response; } } } }) </script> </body> </html>
JS:
//1.引入express const express = require('express'); //2.创建应用 const app = express(); //创建路由规则 app.get('/delay',(request,response)=>{ //设置允许跨域的响应头 response.setHeader('Access-Control-Allow-Origin','*'); //设置一个延时发送响应,3秒发送一个请求到浏览器 setTimeout(()=>{ response.send('延迟了三秒的数据') },3000) }) //监听端口启动服务 app.listen(8000, ()=>{ console.log("服务已经启动, 8000 端口监听中...."); });
8、AJAX取消请求
借助方法:xhr.abort
案例:两个按钮,一个按钮发送请求,一个按钮取消请求
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button>点击发送</button> <button>点击取消</button> <script> //获取元素 绑定事件 const btns = document.querySelectorAll('button'); //给第一个按钮绑定一个发送ajax请求的事件 let x = null; btns[0].onclick = function(){ //创建ajax实例 x = new XMLHttpRequest(); x.open('GET','http://127.0.0.1:8000/delay'); x.send() } //给第二个按钮绑定一个ajax取消事件 btns[1].addEventListener('click',()=>{ x.abort(); }) </script> </body> </html>
9、性能优化,重复发送请求
案例:当用户多次点击一个事件,发送多次请求的时候,服务器会接收到很多相同请求,不利于性能优化,
需求实现:因此,我们在发送请求的时候,可以检测一下,是否之前发送过相同的请求,如果发送过相同的请求,可以把之前发送的请求取消掉,重新发送一个新的请求
代码分析:需要判断是否有正在发送的ajax请求,如果有的话,那么就取消,然后重新发送。因此需要在外部声明一个标识变量,来存储是否有ajax请求,先传入false的值,表示没有,然后进行元素事件绑定,判断是否标识变量为true,是true的话,执行abort方法,然后创建ajax实例,将变量值改为true,然后发送ajax请求,然后进行服务器事件绑定,在服务器事件绑定内部,当readyState状态为4的时候,即服务器响应完成之后,整个ajax交互完成,修改标识变量为false。详情代码如下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button>发送请求</button> <script> const btn = document.querySelector('button'); //13行代码易错点,因为会反复创建ajax实例,因此不能在下面绑定事件内部const创建 let xhr =null; let isSending = false; btn.onclick = function(){ if(isSending) xhr.abort(); xhr = new XMLHttpRequest(); isSending = true; xhr.open('GET','http://127.0.0.1:8000/delay'); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ isSending = false; } } } </script> </body> </html>
易错点13行变量