AJAX 简介
AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML。
通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。
AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
XML 简介
XML 可扩展标记语言。
XML 被设计用来传输和存储数据。
XML 和 HTML 类似,不同的是 HTML 中都是预定义标签,而 XML 中没有预定义标签,
全都是自定义标签,用来表示一些数据。
现在已经被JSON取代了
AJAX 的特点
AJAX 的优点
可以无需刷新页面而与服务器端进行通信。
允许你根据用户事件来更新部分页面内容。
AJAX的缺点
- 没有浏览历史,不能回退
- 存在跨域问题(同源)
- SEO 不友好 (爬虫爬不到)
HTTP
HTTP(hypertext transport protocol)协议『超文本传输协议』,协议详细规定了浏览器和万维网服务器之间互相通信的规则。
约定, 规则
请求报文
重点是格式与参数
1 2 3 4 5 6 7
| 行 POST /s?ie=utf-8 HTTP/1.1 头 Host: atguigu.com Cookie: name=guigu Content-type: application/x-www-form-urlencoded User-Agent: chrome 83 空行 体 username=admin&password=admin
|
响应报文
1 2 3 4 5 6 7 8 9 10 11 12
| 行 HTTP/1.1 200 OK 头 Content-Type: text/html;charset=utf-8 Content-length: 2048 Content-encoding: gzip 空行 体 <html> <head> </head> <body> <h1>啦啦啦</h1> </body> </html>
|
Node.js express 框架
安装nodejs
这里我推荐使用 nvm 来管理 node 的版本,因为在我做项目过程中,可能需要维护别人的老项目,所以node版本经常不匹配,使用 nvm 进行node 版本的管理,可用方便的切换不同版本的 node 。
windows的话,可以在github上下载nvm 打开我们可以看到两个版本
nvm的具体安装和使用简书Do_Du有详细的步骤,可以进行参考
使用express框架
Express 是一个简洁而灵活的 node.js Web应用框架, 提供一系列强大特性帮助你创建各种Web应用
在文件夹路径下的cmd中,初始化项目 npm init --yes
安装 express : npm i express
使用express
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
const express = require('express');
const app = express();
app.get('/', (request, response)=>{ response.send('HELLO EXPRESS'); });
app.listen(8000, ()=>{ console.log("服务已经启动, 8000 端口监听中...."); });
|
写好文件后,在文件所在路径的cmd中 使用 node 来启动服务器:node express基本使用.js
可以看到服务已经启动,cmd中打印出了 “服务已经启动, 8000 端口监听中....
” 字样
测试代码
在下文前端的代码中,发送请求要用的文件为 server.js
是使用 express
框架搭建的,全部代码在如下,跑起来 node
服务后就可以进行 ajax
请求的发送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
| const express = require('express');
const app = express();
app.get('/server', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.send('HELLO AJAX - 2'); });
app.all('/server', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); response.send('HELLO AJAX POST'); });
app.all('/json-server', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); const data = { name: 'atguigu' }; let str = JSON.stringify(data); response.send(str); });
app.get('/ie', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.send('HELLO IE - 5'); });
app.all('/delay', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); setTimeout(() => { response.send('延时响应'); }, 3000) });
app.all('/jquery-server', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); const data = {name:'尚硅谷'}; response.send(JSON.stringify(data)); });
app.all('/axios-server', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); const data = {name:'尚硅谷'}; response.send(JSON.stringify(data)); });
app.all('/fetch-server', (request, response) => { response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); const data = {name:'尚硅谷'}; response.send(JSON.stringify(data)); });
app.all('/jsonp-server',(request, response) => { const data = { name: '尚硅谷atguigu' }; let str = JSON.stringify(data); response.end(`handle(${str})`); });
app.all('/check-username',(request, response) => { const data = { exist: 1, msg: '用户名已经存在' }; let str = JSON.stringify(data); response.end(`handle(${str})`); });
app.all('/jquery-jsonp-server',(request, response) => { const data = { name:'尚硅谷', city: ['北京','上海','深圳'] }; let str = JSON.stringify(data); let cb = request.query.callback;
response.end(`${cb}(${str})`); });
app.all('/cors-server', (request, response)=>{ response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Headers", '*'); response.setHeader("Access-Control-Allow-Method", '*'); response.send('hello CORS'); });
app.listen(8000, () => { console.log("服务已经启动, 8000 端口监听中...."); });
|
原生Ajax
GET请求
启动写好的server.js
: node server.js
点击按钮 ,获取响应显示在div中
创建一个 XMLHttpRequest() 对象
使用 open方法,初始化请求方法和url (对于需要传参数的get请求,直接在url后面拼接参数)
使用send方法来发送请求
绑定onreadystatechange事件,处理返回的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| const btn = document.getElementsByTagName('button')[0]; const result = document.getElementById("result");
btn.onclick = function(){ const xhr = new XMLHttpRequest(); xhr.open('GET', 'http://127.0.0.1:8000/server?a=100&b=200&c=300'); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300){ console.log(xhr.status); console.log(xhr.statusText); console.log(xhr.getAllResponseHeaders()); console.log(xhr.response); result.innerHTML = xhr.response; }else{
} } } }
|
POST请求
过程和get请求差不多,
设置参数的时候,在send方法中设置,
不论get还是post 都可以设置请求头,不过post常用,通过 setRequestHeader来设置请求头
server.js 前端人员不用管其实。但是如果设置了自定义字段的请求头。server.js需要添加response.setHeader('Access-Control-Allow-Headers', '*');
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const result = document.getElementById("result"); result.addEventListener("mouseover", function(){ const xhr = new XMLHttpRequest(); xhr.open('POST', 'http://127.0.0.1:8000/server'); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.setRequestHeader('name','atguigu'); xhr.send('a=100&b=200&c=300'); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300){ result.innerHTML = xhr.response; } } } });
|
处理服务端返回的json数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const result = document.getElementById('result'); window.onkeydown = function(){ const xhr = new XMLHttpRequest(); xhr.responseType = 'json'; xhr.open('GET','http://127.0.0.1:8000/json-server'); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300){ console.log(xhr.response); result.innerHTML = xhr.response.name; } } } }
|
对于处理服务端返回的json格式字符串,我们有两种方法来处理
手动处理
1 2 3 4
| let data = JSON.parse(xhr.response); console.log(data); result.innerHTML = data.name;
|
自动处理
1 2 3
| xhr.responseType = 'json'; console.log(xhr.response);
|
IE 缓存问题
ie 浏览器会把 ajax 请求缓存起来,下次再请求的话,就会请求本地的数据,对于时效性强的页面不好
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const btn = document.getElementsByTagName('button')[0]; const result = document.querySelector('#result'); btn.addEventListener('click', function(){ const xhr = new XMLHttpRequest(); xhr.open("GET",'http://127.0.0.1:8000/ie?t='+Date.now()); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status< 300){ result.innerHTML = xhr.response; } } } })
|
对于缓存问题,只有请求的东西完全一样才会走缓存,所以我们给请求加上一个时间戳参数Date.now()
,这样就每次都是去服务器请求数据。
超时和网络异常问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const btn = document.getElementsByTagName('button')[0]; const result = document.querySelector('#result'); btn.addEventListener('click', function(){ const xhr = new XMLHttpRequest(); xhr.timeout = 2000; xhr.ontimeout = function(){ alert("网络异常, 请稍后重试!!"); } xhr.onerror = function(){ alert("你的网络似乎出了一些问题!"); } xhr.open("GET",'http://127.0.0.1:8000/delay'); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status< 300){ result.innerHTML = xhr.response; } } } })
|
在这个案例中,server.js 中设置了延迟响应来模拟响应超时。
通过 XMLHttpRequest 的timeout 来设置超时的时间,即超过这个时间,就判定它是超时的。
通过 ontimeout 来设置超时的回调函数,即超时后怎么处理
通过 onerror 来设置断网的回调函数。
取消请求
1 2 3 4 5 6 7 8 9 10 11 12
| const btns = document.querySelectorAll('button'); let x = null; btns[0].onclick = function(){ x = new XMLHttpRequest(); x.open("GET",'http://127.0.0.1:8000/delay'); x.send(); }
btns[1].onclick = function(){ x.abort(); }
|
使用 XMLHttpRequest 的abort方法来请求数据
重复请求问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const btns = document.querySelectorAll('button'); let x = null;
let isSending = false; btns[0].onclick = function(){
if(isSending) x.abort(); x = new XMLHttpRequest(); isSending = true; x.open("GET",'http://127.0.0.1:8000/delay'); x.send(); x.onreadystatechange = function(){ if(x.readyState === 4){ isSending = false; } } }
|
通过一个标识变量,如果请求已经发送了,正在加载,就停止后面重复发送的请求。
JQuery 中的ajax请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| $('button').eq(0).click(function(){ $.get('http://127.0.0.1:8000/jquery-server', {a:100, b:200}, function(data){ console.log(data); },'json'); });
$('button').eq(1).click(function(){ $.post('http://127.0.0.1:8000/jquery-server', {a:100, b:200}, function(data){ console.log(data); }); });
$('button').eq(2).click(function(){ $.ajax({ url: 'http://127.0.0.1:8000/jquery-server', data: {a:100, b:200}, type: 'GET', dataType: 'json', success: function(data){ console.log(data); }, timeout: 2000, error: function(){ console.log('出错啦!!'); }, headers: { c:300, d:400 } }); });
|
axios 发送 ajax 请求
axios 是目前发送ajax 请求比较热门的工具包
可以安装也可以直接通过链接引入<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js"></script>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| const btns = document.querySelectorAll('button');
axios.defaults.baseURL = 'http://127.0.0.1:8000';
btns[0].onclick = function () { axios.get('/axios-server', { params: { id: 100, vip: 7 }, headers: { name: 'atguigu', age: 20 } }).then(value => { console.log(value); }); }
btns[1].onclick = function () { axios.post('/axios-server', { username: 'admin', password: 'admin' }, { params: { id: 200, vip: 9 }, headers: { height: 180, weight: 180, } }); } btns[2].onclick = function(){ axios({ method : 'POST', url: '/axios-server', params: { vip:10, level:30 }, headers: { a:100, b:200 }, data: { username: 'admin', password: 'admin' } }).then(response=>{ console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.data); }) }
|
Fetch函数发送请求
fetch函数属于全局对象,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
const btn = document.querySelector('button'); btn.onclick = function(){ fetch('http://127.0.0.1:8000/fetch-server?vip=10', { method: 'POST', headers: { name:'atguigu' }, body: 'username=admin&password=admin' }).then(response => { return response.json(); }).then(response=>{ console.log(response); }); }
|
跨域
同源策略
同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略
同源: 协议、域名、端口号 必须完全相同。
违背同源策略就是跨域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const express = require('express');
const app = express();
app.get('/home', (request, response)=>{ response.sendFile(__dirname + '/index.html'); });
app.get('/data', (request, response)=>{ response.send('用户数据'); });
app.listen(9000, ()=>{ console.log("服务已经启动..."); });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>首页</title> </head> <body> <h1>lalal</h1> <button>点击获取用户数据</button> <script> const btn = document.querySelector('button');
btn.onclick = function(){ const x = new XMLHttpRequest(); x.open("GET",'/data'); x.send(); x.onreadystatechange = function(){ if(x.readyState === 4){ if(x.status >= 200 && x.status < 300){ console.log(x.response); } } } } </script> </body> </html>
|
jsonp 解决跨域
JSONP 是什么
- JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。
JSONP 怎么工作的?
- 在网页有一些标签天生具有跨域能力,比如:img link iframe script。
- JSONP 就是利用 script 标签的跨域能力来发送请求的
JSONP 的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>案例</title> </head> <body> 用户名: <input type="text" id="username"> <p></p> <script> const input = document.querySelector('input'); const p = document.querySelector('p'); function handle(data){ input.style.border = "solid 1px #f00"; p.innerHTML = data.msg; }
input.onblur = function(){ let username = this.value; const script = document.createElement('script'); script.src = 'http://127.0.0.1:8000/check-username'; document.body.appendChild(script); } </script> </body> </html>
|
通过js 创建一个script 标签,在标签的src中写入需要跨域的路径,然后服务器返回一个js格式的数据实现跨域效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
app.all('/check-username',(request, response) => { const data = { exist: 1, msg: '用户名已经存在' }; let str = JSON.stringify(data); response.end(`handle(${str})`); });
|
通过jquery实现 jsonp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>jQuery-jsonp</title> <style> #result{ width:300px; height:100px; border:solid 1px #089; } </style> <script crossorigin="anonymous" src='https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js'></script> </head> <body> <button>点击发送 jsonp 请求</button> <div id="result">
</div> <script> $('button').eq(0).click(function(){ $.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){ $('#result').html(` 名称: ${data.name}<br> 校区: ${data.city} `) }); }); </script> </body> </html>
|
发送请求的时候,需要加一个callback参数,返回的时候,获取到该参数,改参数就是getjson里面的回调函数的意思,返回对回调函数的调用,就可以实现对数据的渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
app.all('/jquery-jsonp-server',(request, response) => { const data = { name:'尚硅谷', city: ['北京','上海','深圳'] }; let str = JSON.stringify(data); let cb = request.query.callback;
response.end(`${cb}(${str})`); });
|
CORS 跨域
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
CORS 是什么?
- CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些 源站通过浏览器有权限访问哪些资源
CORS 怎么工作的?
- CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行
CORS 的使用
前端就按照ajax 的方法写就好了,cors 是在后端的处理。在后端设置一个响应头,允许跨域
1 2 3 4 5 6 7 8
| app.all('/cors-server', (request, response)=>{ response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Headers", '*'); response.setHeader("Access-Control-Allow-Method", '*'); response.send('hello CORS'); });
|