Ajax基础知识(尚硅谷)
-
搜索框的联想
-
重复用户名的提醒
-
懒加载的效果
-
朋友圈往下翻到底,再加载新的内容
-
用户不需要刷新、跳转就能在网页上有服务器返回的内容…
一、原生Ajax
异步的js和XML,不需要整体页面刷新再等服务器返回,而是每个部分都可以向服务器发请求得数据。
1.2 Ajax用到的数据格式
原来使用的是XML,但现在使用的是JSON
1.3 Ajax优缺点
优点:无需刷新页面与服务端进行通信,允许根据用户事件来更新页面中的部分内容
缺点:没有浏览历史(不能回退),存在跨域问题(同源),SEO(搜索引擎优化)不友好
1.4 HTTP协议
请求报文:
请求行: get / post … ?param1=xxx… http/1.1
请求头: Host: …
Cookie: …
Content-Type: …
User-Agent: …浏览器信息
空行
请求体: post时携带表单信息
响应报文:
响应行: HTTP/1.1 状态码(200) 状态说明(OK)
响应头: Content-Type: text/html;charset=utf-8
Content-length: …
Content-encoding: …
空行
响应体: 静态HTML页面
安装Nodejs和Express框架…
1.5 Nodejs和Express框架
Nodejs可以在本地创建一个简易的服务器端,Express是一个服务器端框架,装上Express后Node服务器端就可以在一个熟知端口监听request了。
npm init --yes //对新建项目进行初始化(在项目文件夹中加载node_modules)
npm install express //给当前项目安装express框架
//1. 引入Express框架
const express = require('express');
//2. 创建应用对象
const app = express();
//3. 创建路由规则
app.get('/',(request,response) => {
//path参数配置为'/'全局路径
response.send('hello express!');
});
//4. 监听端口启动服务
app.listen(8000,() => {
//侦听指定的端口号
console.log('服务已经启动,8000端口监听中');
})
端口如果被占用报错,检查一下是不是有其它nodejs正在占用这个端口。
1.6 Ajax发送请求、接收响应
用于展示的页面
<!DOCTYPE html>
<html>
<head>
<title>ajax get请求</title>
<meta charset="utf-8">
<style>
#res{
width: 200px;
height: 150px;
border: solid 1px #90b;
}
</style>
</head>
<body>
<button id="btn">点击发送请求</button>
<div id="res"></div>
<script>
const btn = document.getElementsByTagName('button')[0];
const res = document.getElementById('res');
btn.onclick = function(){
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化:设置请求方法和url
xhr.open('GET','http://127.0.0.1:8000/server');
//3. 发送
xhr.send();
//4. 事件绑定 处理服务器返回的结果
//readystate:xhr对象中的属性,状态0,1,2,3,4
xhr.onreadystatechange = function(){
//状态4:服务端返回了所有结果
if(xhr.readyState === 4){
//判断响应状态码
if(xhr.status >= 200 && xhr.status < 300){
//正常响应
//1. 响应行
console.log('状态码:',xhr.status);
console.log('状态字符串:',xhr.statusText);
console.log('响应头:',xhr.getAllResponseHeaders());
console.log('响应体',xhr.response);
res.innerHTML = xhr.response; //将server.js返回的请求内容展示在div中
}else{
}
}
}
}
</script>
</body>
</html>
server.js
//1. 引入Express框架
const express = require('express');
//2. 创建应用对象
const app = express();
//3. 创建路由规则
app.get('/server',(request,response) => {
response.setHeader('Access-Control-Allow-Origin','*'); //设置允许跨域
response.send('hello ajax!');
});
//4. 监听端口启动服务
app.listen(8000,() => {
console.log('服务已经启动,8000端口监听中');
})
1.7 Ajax请求带参数
在初始化xhr对象时指定url的后面带上参数即可
1.8 Ajax发送post请求
需要在服务器端配置好post方法的响应
//1. 引入Express框架
const express = require('express');
//2. 创建应用对象
const app = express();
//3. 创建路由规则
app.get('/server',(request,response) => {
response.setHeader('Access-Control-Allow-Origin','*'); //设置允许跨域
response.send('hello ajax!');
});
app.post('/server',(request,response) => {
response.setHeader('Access-Control-Allow-Origin','*'); //设置允许跨域
response.send('hello ajax POST!');
})
//4. 监听端口启动服务
app.listen(8000,() => {
console.log('服务已经启动,8000端口监听中');
})
使用POST请求,可以send任意类型数据
<!DOCTYPE html>
<html>
<head>
<title>ajax get请求</title>
<meta charset="utf-8">
<style>
#res{
width: 200px;
height: 150px;
border: solid 1px #90b;
}
</style>
</head>
<body>
<div id="res"></div>
<script>
const res = document.getElementById('res');
res.addEventListener('mouseover',function(){
const xhr = new XMLHttpRequest();
xhr.open('POST','http://127.0.0.1:8000/server');
xhr.send('121212123'); //这里可以传任意类型数据
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
res.innerHTML = xhr.response;
}
}
}
})
</script>
</body>
</html>
1.9 Ajax设置请求头信息
向服务器端发请求时,为了直到服务器接收什么类型的请求,浏览器会先发送一个options请求来探查请求方法和服务器性能。
所以要在服务器端配置好接收options请求的属性
app.all('/server',function(){
... //设置all属性可以接收所有类型的请求,如果请求头中有自定义头消息,就可以正确地接收了
})
1.10 Ajax服务端响应JSON数据
服务器端响应json格式数据
app.all('/json-server',(request,response) => {
response.setHeader('Access-Control-Allow-Origin','*');
const data = {
name:'Tom'
};
let str = JSON.stringify(data); //先将json对象转换为字符串
response.send(str);
});
页面接收可以手动进行数据类型转换,也可以配置自动转换
const res = document.getElementById('res');
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){
//手动数据类型转换
//let data = JSON.parse(xhr.response);
//res.innerHTML = data.name;
//自动转化,直接在这里配置接收response的name属性
res.innerHTML = xhr.response.name;
}
}
}
}
1.11 Ajax的IE缓存问题
IE浏览器会将上次请求后得到的响应内容存储在本地缓存中,下次就不会再向服务器发请求了,而是直接采用缓存,这就导致服务器变更与浏览器本地得到的响应不同步。
对于IE浏览器,在url的后面加上一个时间戳参数可以让IE浏览器发送新请求,从而避免上面的问题。
xhr.open('GET','http://127.0.0.1:8000/ie?t='+Date.now());
//参数t即为时间戳
1.12 Ajax请求超时和网络异常
可以给Ajax请求设置请求超时时限,当服务器在设定时间后仍然不能返回响应,则自动取消本次请求。
xhr.timeout = 2000; //设置超过2s后请求取消
还可以给Ajax请求超时设置回调函数。
//超时回调
xhr.ontimeout = function(){
alert('网络异常,请稍后重试!');
};
当本地网络出现异常,我们不希望没有任何提示,所以可以给Ajax请求加上网络异常回调
//网络异常回调
xhr.onerror = function(){
alert('你的网络似乎有些问题');
}
1.13 Ajax请求取消
手动取消Ajax请求
<!DOCTYPE html>
<html>
<head>
<title>请求取消</title>
<meta charset="utf-8">
<style>
#res{
width: 200px;
height: 150px;
border: solid 1px #90b;
}
</style>
</head>
<body>
<button>点击发送请求</button>
<button>点击取消请求</button>
<script>
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(); //取消ajax请求
}
</script>
</body>
</html>
1.12 Ajax请求重复发送问题
对同一个服务器进程可能会有多个重复请求,这无疑会增加服务器的负担,所以在浏览器这里配置好对一个服务器进程发送一次只能发送一个请求,上一个请求-响应没有结束则不能发送新的请求。
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();
}
二、jQuery的Ajax
2.1 jQuery发送Ajax请求
用jQuery可以发送GET \ POST \ 通用类型的请求
<!DOCTYPE html>
<html>
<head>
<titel>jQuery发送ajax请求</titel>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<div>
<h2>jQuery发送ajax请求</h2>
<button>GET</button>
<button>POST</button>
<button>通用方法ajax</button>
</div>
<script>
$('button').eq(0).click(function(){
//jquery的get方法有若干个参数,第一个是url,第二个是请求参数,第三个是一个回调函数(参数为响应体)
//第四个是响应体数据格式
$.get('http:127.0.0.1:8000/jquery-server', {
a:100,b:200}, function(data){
//data为响应体
console.log(data);
},'json') //将响应体格式设置为json
})
$('button').eq(1).click(function(){
//jquery的get方法有三个参数,第一个是url,第二个是请求参数,第三个是一个回调函数(参数为响应体)
$.post('http:127.0.0.1:8000/jquery-server', {
a:100,b:200}, function(data){
//data为响应体
console.log(data);
})
})
</script>
</body>
</html>
//jQuery服务对应的路由规则
app.all('/jquery-server',(request,response) => {
response.setHeader('Access-Control-Allow-Origin','*'); //设置允许跨域
const data = {
name:'Tom'
}
let str = JSON.stringify(data);
response.send(str);
});
2.2 jQuery发送Ajax通用请求方法
除了直接发送get和post请求外,还有通用的ajax发送方法。
$('button').eq(2).click(function(){
$.ajax({
//url
url:'http:127.0.0.1:8000/jquery-server',
//请求参数
data: {
a:100,b:200},
//请求方式
type: 'GET',
//收到响应后的回调函数,参数为响应体
success: function(data){
console.log(data);
},
//失败的回调
error: function(){
cnosole.log('出错了');
},
//超时时间
timeout: 2000,
//头信息(注意服务器要设置Access-Control-Allow-Headers,*)
headers: {
c:300,
d:400
}
})
})
三、Axios的Ajax
3.1 axios发送Ajax请求
axios是vue中ajax的使用方式,可以指定get或post方式发送请求
const btns = document.querySelectorAll('button');
//配置baseURL可以简化url
axios.defaults.baseURL = 'http://127.0.0.1:8000';
//axios.get(url,others)
btns[0].onclick = function(){
axios.get('/axios-server',{
//请求参数
params:{
id:100,
a:101
},
//请求头信息
headers:{
name:'Tom',
age:21
}
}).then(value => {
console.log(value);
//这里是Promise的结构,等价于jQuery和原生方法中的回调
//value为响应体
})
}
//post的参数与get有区别,要注意
//axios.post(url,data,others)
btns[1].onclick = function(){
axios.post('/axios-server',{
username:'admin',
password:'123'
},{
params:{
id:100,
a:101
},
headers:{
name:'Tom',
age:21
}
})
}
四、fetch函数发送Ajax请求
fetch是一个js全局函数,也能够做到发送ajax请求。
//fetch是一个全局方法
//fetch(url, config={method, headers, body})
fetch('http://127.0.0.1:8000/fetch-server',{
//请求方法
method: 'POST',
//请求头
headers: {
name: 'Tom'
},
//请求体
body: 'username=admin&password=123'
}).then(response => {
//这里类似于axios,也采用Promise的then进行回调
return response.text(); //这里response是一个对象,为了控制台能够呈现字符串内容,需要把它转化为字符串形式
//return response.json(); //如果返回的是一个json对象,那么就把它转换为json格式
}).then(response => {
console.log(response); //这里的repsonse就是上一个then的返回值
})
五、跨域
5.1 同源策略
同源:浏览器请求的url必须和ajax请求的url是同一个(协议、域名、端口号完全一致)不一样就跨域
单台计算机性能有限,所以跨域是无法避免的。
如果在服务器中不配置跨域
app.get('/home',(request,response) => {
//响应一个页面
response.sendFile(__dirname+'/01_demo.html');
//console.log('收到了请求');
});
app.get('/data',(request,response) => {
response.send('用户数据');
})
那么在通过第一个get响应后得到的页面上,直接发送第二个请求
const btn = document.querySelector('button');
btn.onclick = function(){
const xhr = new XMLHttpRequest();
//因为这里是同源策略,所以协议域名端口都是浏览器自动加上去的
xhr.open('get','/data');
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response);
}
}
}
}
这就是同源请求。
5.2 JSONP
jsonp利用了script标签能够跨域访问的特性,实现ajax跨域。但是jsonp只针对get请求跨域。
<div id="box"></div>
<script>
function handle(data){
const res = document.getElementById('box');
res.innerHTML = data.name;
}
</script>
<script src="http://127.0.0.1:8000/jsonp-server"></script>
<!--script只能解析js代码,所以接收到的必须是表达式形式的内容-->
//jsonp服务
app.all('/jsonp-server',(request,response) => {
const data = {
msg:'用户名已存在',
exist: 1
};
let str = JSON.stringify(data);
response.send(`handle(${
str})`); //返回的是一段js代码,其中调用了用户页面js代码中的handle函数
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原生jsonp</title>
</head>
<body>
用户名:<input type="text" /><br/>
<span></span>
<script>
const input = document.querySelector('input');
const span = document.querySelector('span');
function handle(data){
//服务器发回来的响应会调用这个函数
input.style.border = "solid 1px #f00";
span.innerHTML = data.msg;
}
input.onblur= function(){
let username = input.value; //获取用户名
const script = document.createElement('script'); //创建script标签,用于jsonp
script.src = "http://127.0.0.1:8000/jsonp-server"; //配置好请求的url
document.body.appendChild(script);
}
</script>
</body>
</html>
5.3 jQuery发送jsonp请求
服务端要做出一些改变,因为用jQuery进行jsonp请求时url会携带一个callback参数,它指明了回调函数。
//jQuery-jsonp服务
app.all('/jquery-jsonp-server',(request,response) => {
const data = {
name:'Tom',
city:['太原','成都','北京']
};
let str = JSON.stringify(data);
let cb = request.query.callback; //获得回调函数
response.send(`${
cb}(${
str})`);
})
<body>
<button>点击发送jsonp请求</button>
<div id="res"></div>
<script>
$('button').eq(0).click(function(){
//注意getJSON方法的url中必须配置callback参数
$.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?',function(data){
$('#res').html(`
名称:${
data.name}
校区:${
data.city}`
)
})
})
</script>
</body>
5.4 CORS跨域
CORS (跨越资源共享) 是官方的解决方案(不同于jsonp)
在服务器端配置响应头Access-Control-Control-Origin
//CORS服务
app.all('/cors-server',(request,response) => {
response.setHeader('Access-Control-Allow-Origin','*'); //配置该响应头字段就会使用CORS,*代表对于任何url都可接收
response.setHeader('Access-Control-Allow-Headers','*'); //配置该响应头字段就会允许自定义请求头字段的请求访问
response.send('hello CORS!');
});