前言
本文详细描述了Ajax的基本知识,包括Ajax的基本请求、Ajax请求的封装、请求的数据格式问题、状态码等,此外还涉及了跨域问题与Ajax拦截器的实现问题并引用具体例子加深理解学习
1.概述
- Ajax它是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高用户浏览网站应用的体验。
- 应用场景:
- 页面上拉加载更多数据
- 无刷新分页
- 表单项离开焦点数据验证
- 搜索框提示文字下拉列表
- 缺点:Ajax本身不支持跨域请求
2. ajax请求的基本过程
var xhr = new XMLHttpRequest();
xhr.open('method','url',async(可省略));
xhr.send();
xhr.onload = function (){}
2.1 创建ajax对象
var xhr = new XMLHttpRequest()
2.2 设置请求
xhr.open(method,url,async)
:ajax请求方式:get或post,以及请求地址url,是否异步async(默认为true)
使用post请求
注意:若使用post方法则必须设置请求的参数格式类型,.setRequestHeader(header, value)
header:属性的名称。
value:属性的值。
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
发送表单类型的资料:
('Content-Type','application/x-www-form-urlencoded')
数据格式: name=小明&age=20
xhr.send('username=admin&age=18')
发送JSON 类型的资料:( 要将json 对象转换为 ,因为请求参数必须要以字符串格式传递)
('Content-Type','application/json')
数据格式:{name: '小明', age: '20'}
xhr.send(JSON.stringify({name: 'admin', age:18}));
2.3 发送请求
xhr.send()
2.4 获取服务器端响应到客户端的数据
xhr.onload = function (){
console.log(xhr.responseText)
// xhr.responseText 接收文本格式的响应数据
// xhr.responseXML 接收 xml 格式的响应数据
}
3. 数据格式
3.1 JSON.parse()
- 在 http 请求与响应的过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会被转换为进行传输,故获取时要用
JSON.parse()
重新转为json对象
JSON.parse() // 将 json 字符串转换为json对象
3.2 JSON.stringify()
注意:get 请求是不能提交 json 对象数据格式的,要转为json字符串
JSON.stringify() // 将json对象转换为json字符串
数字Infinity和NaN以及值 null使用JSON.stringify()时都会转为null
JSON.stringify([NaN, null, Infinity]); // '[null,null,null]'
JSON.stringify() 与 localStorage存取
const session = {
'screens': [
{ 'name': 'screenA', 'width': 450, 'height': 250 },
{ 'name': 'screenB', 'width': 650, 'height': 350 },
{ 'name': 'screenC', 'width': 750, 'height': 120 }
],
'state': true
};
localStorage.setItem('session',JSON.stringify(session) );
const restoredSession = JSON.parse(localStorage.getItem('session'));
console.log(restoredSession);
若没用JSON.stringify()存数据 与JSON.parse()取数据:
localStorage.setItem('session', session);
const restoredSession = localStorage.getItem('session');
console.log(restoredSession);
若取数据时没用JSON.parse()取数据
localStorage.setItem('session', JSON.stringify( session));
const restoredSession = localStorage.getItem('session');
console.log(restoredSession);
若存数据时没用JSON.stringify()会报错
4. Ajax状态码
- 0:请求未初始化(还没有调用open())
- 1:正在加载:已建立服务器连接但还没有发送
(已调用open,还没有调用send()) - 2:请求已经发送,已加载
- 3:请求正在处理中,通常响应中已经有部分数据可以用了
- 4:响应已经完成,可以获取并使用服务器的响应了
xhr.readyState // 获取Ajax状态码
xhr.onprogress
是状态码为3的时候,此时可以写加载动画等效果,如页面跳转时的小圆点加载,xhr.onprogress为非必写函数
xhr.onprogress = function(){
console.log('READYSTATE: ', xhr.readyState);
}
常见状态码
- 200 表示从客户端发来的请求在服务器端被正常处理
- 204 表示请求处理成功,但没有资源返回。
- 301 表示永久性重定向。该状态码表示请求的资源已被分配了新的URI,以后应使用资源现在所指的URI。
- 302 表示临时性重定向。
- 401 表示未授权(Unauthorized),当前请求需要用户验证
- 403 表示服务器拒绝请求
- 404 表示服务器找不到请求的网页
- 503 表示服务器暂时处于超负载或维护,无法处理请求。
5. onreadystatechange 事件与onload事件的区别
onreadystatechange :
当 Ajax 状态码发生变化时将自动触发onreadystatechange事件,当状态码为 4 时就可以通过xhr.responseText
获取服务器端的响应数据了。
// 当Ajax状态码发生变化时
xhr.onreadystatechange = function () {
// 判断当Ajax状态码为4时
if (this.readyState == 4 && this.status == 200) {
// 获取服务器端的响应数据
console.log(xhr.responseText);
}
}
使用onreadystatechange可以打印状态码 2、3、4
onload:
只有处于状态码4,即响应已就绪的情况下才会进入onload,使用onload只打印状态码4
6. Ajax错误处理
xhr.onerror = function () {
alert('Ajax请求出错')
}
//当请求遇到错误时触发该事件
- 网络畅通,服务器端能接收到请求,服务器端返回的结果不是预期结果。用
xhr.status
获取http状态码进行排查 - 网络畅通,服务器端没有接收到请求,返回404状态码:检查请求地址是否错误。
- 网络畅通,服务器端能接收到请求,服务器端返回500状态码:服务器端错误,找后端程序员进行沟通。
xhr.open('get', 'http://www.example.com?t=' + Math.random());
7. ajax实例运用
:若用本地文件调试ajax可以用open with live server打开,否则会报错,因为浏览器认为此种方式不安全,将此视为跨站点攻击,并会阻止请求
推荐一个可以免费请求数据的ajax接口网站用于测试:FillText.com
7.1 ajax获取纯文本
<button id="button">获取纯文本</button>
<br><br>
<div id="text"></div>
<script>
document.getElementById('button').addEventListener('click', loadText);
function loadText(){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'sample.txt');
xhr.onload = function(){
console.log('READYSTATE: ', xhr.readyState);
if(this.status == 200){
document.getElementById('text').innerHTML = this.responseText;
} else if(this.status = 404){
document.getElementById('text').innerHTML = 'Not Found';
}
}
xhr.onerror = function(){
console.log('请求出错');
}
xhr.send();
}
</script>
若改为不存在的文本文件会报错 xhr.open('GET', 'sample2.txt');
7.2 ajax获取本地JSON文件
<body>
<button id="button">Get Users</button>
<h1>Users</h1>
<div id="users"></div>
<script>
document.getElementById('button').addEventListener('click', loadUsers);
function loadUsers() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'users.json');
xhr.onload = function () {
var users = JSON.parse(this.responseText);
var output = '';
for (var i in users) {
output += `
<ul>
<li>ID:${users[i].id}</li>
<li>Name: ${users[i].name}</li>
</ul>
`;
}
document.getElementById('users').innerHTML = output;
};
xhr.send();
}
</script>
</body>
JSON文件:
[
{
"id":1,
"name":"Rick"
},
{
"id":2,
"name":"Wang"
},
{
"id":3,
"name":"Fang"
}
]
7.3 ajax获取外部链接api
<script>
document.getElementById('button').addEventListener('click', loadUsers);
function loadUsers(){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.filltext.com/?rows=6&fname={firstName}&lname={lastName}');
xhr.onload = function(){
var users = JSON.parse(this.responseText);
console.log(users);
var output = '';
for(var i in users){
output +=
'<div class="user">' +
'<ul>' +
'<li>fname: '+users[i].fname+'</li>' +
'<li>lname: '+users[i].lname+'</li>' +
'</ul>' +
'</div>';
}
document.getElementById('users').innerHTML = output;
}
xhr.send();
}
</script>
7.4 jquery中使用ajax
$('#btn').on('click', function () {
$.ajax({
type: 'post',
url: '',
// 向服务器端发送的请求参数
data: JSON.stringify(params),
success: function (xhr) {
console.log(xhr);
}
})
});
8. ajax封装
参考黑马程序员教程,我们抽离一个 ajax.js 文件,这样使用ajax只需引入并传递相应的参数,大大节省代码量
Object.assign(target, ...sources)
:
target:目标对象,接收源对象属性的对象,也是修改后的返回值。
sources:源对象,包含将被合并的属性。
如果目标对象与源对象具有相同的 key,则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的属性。
封装:
<script>
function ajax (options) {
// 存储的是默认值
var defaults = {
type: 'get',
url: '',
data: {},
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
success: function () {},
error: function () {}
};
// 使用options对象中的属性覆盖defaults对象中的属性
Object.assign(defaults, options);
var xhr = new XMLHttpRequest();
// 拼接请求参数的变量
var params = '';
// 循环用户传递的参数,即对对象的循环遍历与取值
for (var value in defaults.data) {
// 将参数转换为字符串格式
params += value + '=' + defaults.data[value ] + '&';
//value为对象属性名,defaults.data[value ]为对应的属性值
}
// 将参数最后面的&截取掉
params = params.substr(0, params.length - 1);
// 判断请求方式
if (defaults.type == 'get') {
defaults.url = defaults.url + '?' + params;
}
// 配置ajax对象
xhr.open(defaults.type, defaults.url);
// 如果请求方式为post
if (defaults.type == 'post') {
// 设置请求参数格式的类型
var contentType = defaults.header['Content-Type']
//xhr.setRequestHeader在xhr.open之后,xhr.send之前
xhr.setRequestHeader('Content-Type', contentType);
// 如果类型为json
if (contentType == 'application/json') {
xhr.send(JSON.stringify(defaults.data))
}else {
// 向服务器端传递普通类型的请求参数
xhr.send(params);
}
}
else {
// 发送get请求
xhr.send();
}
// 当xhr对象接收完响应数据后触发
xhr.onload = function () {
// 获取响应头中的数据类型
var contentType = xhr.getResponseHeader('Content-Type');
// 服务器端返回的数据
var responseText = xhr.responseText;
// 如果响应类型为json
if (contentType.includes('application/json')) {
// 将json字符串转换为json对象
responseText = JSON.parse(responseText)
}
if (xhr.status == 200) {
// 请求成功 调用处理成功情况的函数
defaults.success(responseText, xhr);
}else {
// 请求失败 调用处理失败情况的函数
defaults.error(responseText, xhr);
}
}
// 当网络中断时
xhr.onerror = function () {
// 调用失败回调函数并且将xhr对象传递给回调函数
defaults.error(xhr);
}
}
</script>
使用:
<body>
<script src="/js/ajax.js"></script>
<script type="text/javascript">
ajax({
url: '',
type: '',
success: function(data) {
console.log(data)
}
})
</script>
</body>
9. 跨域问题解决
1、 通过jsonp跨域
2、 跨域资源共享(CORS)
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 document.domain + iframe跨域
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
详见博客:前端跨域解决方案
10. 使用ajax实现拦截器
拦截器的设置可以让我们在发出 request 或接到 response 之前做一些事情,如改变 response的数据格式或是根据不同 request 来添加不同的config 等。
具体实现方法:Ajax拦截器的实现