AJAX基础知识及核心原理

AJAX基础知识及核心原理

AJAX基础知识

什么是AJAX?

async javascript and xml,异步的JS和XML

异步的 JS

这里的异步不是说 ajax 只能基于异步进行请求(虽然建议都是使用异步编程),这里的异步特指的是局部刷新

xml:可扩展的标记语言

作用是用来存储数据的(通过自己扩展的标记名称清晰的展示出数据结构);ajax 之所以称为异步的 js 和 xml,主要是因为 当初最开始用 ajax 实现客户端和服务器端数据通信的时候,传输的数据格式一般都是 xml 格式的数据,我们把它称为异步 js 和 xml。(现在一般都是基于 JSON 格式来进行数据传输的)

局部刷新

局部刷新 VS 全局刷新:

在非完全前后端分离项目中,前端开发只需要完成页面的制作,并且把一些基础的人机交互效果使用 js 完成即可,页面中需要动态呈现内容的部分,都是交给后台开发工程师做数据绑定和基于服务器进行渲染的(服务器端渲染)

【优势】:

1、动态展示的数据在页面的源代码中可以看见,有利于 SEO 优化推广(有利于搜索引擎的收录和抓取)

2、从服务器端获取的结果就已经是最后要呈现的结果了,不需要客户端做额外的事情,所以页面加载速度快(前提是服务器端处理的速度够快,能够处理过来),所以类似于京东、淘宝这些网站,首屏数据一般都是经由服务器端渲染的

【弊端】:

1、如果页面中存在需要实时更新的数据,每一次想要展示最新的数据,页面都要重新的刷新一次,这样肯定是不行的

2、都交给服务器端做数据渲染,服务器端的压力太大,如果服务器处理不过来,页面呈现的速度更慢

3、这种模式不利于开发(开发效率低)

目前大部分项目都是前后端完全分离的项目:

前后端完全分离的项目,页面中需要动态绑定的数据是交给客户端完成渲染的

1、向服务器端发送 AJAX 请求

2、把从服务器端获取的数据解析处理,拼接成为我们需要展示的 HTML 字符串

3、把拼接好的字符串替换页面中某一部分的内容(局部刷新),页面整体不需要重新加载,局部渲染即可

【优势】:

1、我们可以根据需求,任意修改页面中某一部分的内容(例如实时刷新),整体页面不刷新,性能好,体验好(所有表单验证。需要时时刷新等需求都要基于ajax实现)

2、有利于开发,有利于开发的效率(可以同时进行开发)

【弊端】:

1、不利于 SEO 优化:第一次从服务器端获取的内容不包含需要动态绑定的数据,所以页面的源代码中没有这些内容,不利于 SEO 收录,后期通过 JS 添加到页面中的内容,并不会写在页面的源代码中(是源代码不是页面结构)

2、交由客户端渲染,首先需要把页面呈现,然后再通过 JS 的异步 AJAX 请求获取数据,然后数据绑定,浏览器再把动态增加的部分重新渲染,无形中浪费了一些时间,没有服务器端渲染页面呈现速度快

基于原生 JS 实现 AJAX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//	1.创建一个 AJAX 对象
let xhr = new XMLHttpRequest(); // 不兼容 IE6 及更低版本浏览器(IE6:ActiveXObject)

// 2.打开请求地址(可以理解为一些基础配置,但是并没有发送请求)
xhr.open([method],[url],[async],[user name],[user password]);

// 3.监听 AJAX 状态改变,获取响应信息(获取响应头信息、获取响应主体信息)
xhr.onreadystatechange=()=>{
if (xhr.readyState===4 && xhr.status===200){
let result = xhr.responseText; // 获取响应主体中的内容
}
};

// 4.发送 AJAX 请求(括号中传递的内容就是请求主体的内容)
xhr.send(null);

分析第二步中的细节点

xhr.open([method],[url],[async],[user name],[user password]);

【AJAX请求方式 (method)】:

1、GET 系列请求(获取)

​ get、delete(从服务器上删除某些资源文件)、head(只想获取服务器返回的响应头信息,响应主体内容不需要获取)……

2、POST 系列请求(推送)

​ post、put(向服务器中增加指定的资源文件)……

我们想获取一些动态展示的信息,一般使用GET请求,因为只需要向服务器端发送请求,告诉服务器端我们想要什么,服务器端就会把需要的数据返回

在实现注册功能的时候,我们需要把用户输入的信息发送给服务器进行存储,服务器一般返回成功还是失败等状态,此时我们一般都是基于 POST 请求完成的

GET 系列请求和 POST 系列请求,在项目实战中存在很多的区别:

1、GET 请求传递给服务器的内容一般没有 POST 请求传递给服务器的内容多

​ 原因:GET 请求传递给服务器内容一般都是基于 url地址问号传递参数 来实现的,而 POST 请求一般都是基于 设置请求主体 来实现的。各浏览器都有自己的关于URL最大长度的限制(谷歌:8KB、火狐:7KB、IE:2KB……)超过限制长度的部分,浏览器会自动截取掉,导致传递给服务器的数据缺失.

​ 理论上 POST 请求通过请求主体传递是没有大小限制的,真实项目中为了保证传输的速率,我们也会限制大小(例如:上传的资料或者图片我们会做大小的限制)

2、GET 请求很容易出现缓存(这个缓存不可控:一般我们都不需要),而 POST 不会出现缓存(除非自己做特殊处理);

​ 原因:GET 是通过 URL 问号传参传递给服务器信息,而 POST 是设置请求主体;设置请求主体不会出现缓存,但是 URL 传参就会。

​ 解决方案:每一次重新请求的时候,在URL的末尾追加一个随机数,保证每一次请求的地址不完全一致,就可以避免是从缓存中读取的数据

3、GET 请求没有 POST 请求安全( POST也并不是十分安全,只是相对安全)

​ 原因:还是因为 GET 是 URL 传参给服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*  GET 请求的缓存问题  */

// 每隔一分钟重新请求服务器最新的数据,然后展示在页面中(页面某些数据实时刷新)
setTimeout(()=>{
$.ajax({
url: 'getList?name=xxx',
// ...
success: function(result){
// 第一次请求数据回来,间隔一分钟后,浏览器又发送一次请求,但是新发送的请求,不管是地址还是传递的参数都和第一次一样,浏览器很有可能会把上一次数据获取,而不是获取最新的数据
}
})
}, 60000)

// 解决方案:每一次重新请求的时候,在URL的末尾追加一个随机数,保证每一次请求的地址不完全一致,就可以避免是从缓存中读取的数据
setTimeout(()=>{
$.ajax({
url: 'getList?name=xxx&_='+Math.random(),
// ...
success: function(result){

}
})
},60000)

【请求数据地址 (URL)】:

URL:请求数据的地址(API地址),真实项目中,后台开发工程师会编写一个 API 文档,在 API 文档中汇总了获取那些数据需要使用那些地址,我们按照文档操作即可

【是否同步请求 (async)】:

async:异步(sync 同步),设置当前 ajax 请求时异步的还是同步的,不写默认是异步(true),如果设置为 false,则代表当前请求是同步的

【用户名和密码】:

用户名和密码:这两个参数一般不用,如果请求的 URL 地址所在的服务器设定了访问权限,则需要我么提供可通行的用户名和密码才可以(一般服务器都是可以允许匿名访问的)

第三部分中的细节点

1
2
3
4
5
6
//	3.监听AJAX状态改变,获取响应信息(获取响应头信息、获取响应主体信息)
xhr.onreadystatechange=()=>{
if (xhr.readyState===4 && xhr.status===200){
let result = xhr.responseText; // 获取响应主体中的内容
}
};

AJAX状态码:(xhr.readyState)描述当前 AJAX 操作的状态的

0:UNSENT 未发送,只要创建一个 ajax 对象,默认值就是零

1:OPENED 我们已经执行了 xhr.open 这个操作

2:HEADERS_RECEIVED 当前 ajax 的请求已经发送,并且已经接收到服务器端返回的响应头信息了

3:LOADING 响应主体内容正在返回的路上

4:DONE 响应主体内容已经返回到客户端

HTTP网络状态码: (xhr.status)记录了当前服务器返回信息的状态码

200:成功,一个完整的 HTTP 事物完成(以2开头的状态码一般都是成功)

以3开头的一般也是成功 ,只不过是服务器端做了很多特殊的处理

301:Moved Permanentntly 永久转移(永久重定向)一般应用于域名迁移

302:Move temporarily 临时转移(临时重定向,新的HTTP版本中任务307是临时重定向)一般应用于服务器的负载均衡(一般图片请求经常出现302,很多公司都有单独的图片服务器)

304:Not Modified 从浏览器缓存中获取数据(把一些不经常更新的文件或者内容缓存到浏览器中,下一次从缓存中获取,减轻服务器压力,也提高页面加载速度)

以4开头的一般都是失败,而且客户端的问题偏大

400: 请求参数错误

402: 无权限访问

404: 访问地址不存在

以5开头的,一般都是失败,而且服务器的问题偏大

500: Internal Server Error 未知的服务器错误

503: Service Unavailable 服务器超负载

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
//	ajax 中总共支持几个方法?
let xhr = new XMLHttpRequest();
console.dir(xhr);

/*
【属性】:(常用的几个)
1、readyState:存储的是当前 ajax 的状态码
2、response/responseText/responseXML:都是用来接收服务器返回的响应主体中的内容,只是根据服务器返回内容的格式不一样,我们使用不同的属性接收即可
responseText 是最常用的,接收到的结果是字符串格式的
responseXML 偶尔会用到,如果服务器端返回的是 XML 文档数据,需要使用这个属性来接收
3、status:记录了服务器返回的 HTTP 状态码
statusText:对返回状态码的描述
4、timeout:设置当前 AJAX 请求的超时时间,假设设置时间为 3000,从ajax请求发送开始,3秒后响应主体内容还没有 返回,浏览器会把当前 ajax 请求任务强制断开

【方法】:(常用的几个)
1、abort():强制中断 ajax 请求
2、getAllResponseHeaders():获取全部的响应头信息(获取到的结果是一堆字符串文本)
3、getResponseHeader(key):获取指定属性名的响应头信息
4、open():打开一个 URL 地址
5、overrideMimeType():重写数据的 MIME 类型
6、send():发送 ajax 请求(括号中书写的内容是客户端基于请求主体把信息传递给服务器)
7、setRequestHeader(key,value):设置请求头信息(可以是设置的自定义请求头信息)

【事件】:(常用的几个)
1、onabort;当 ajax 被中断请求触发这个事件
2、onreadystatechange:ajax 状态发生改变,会触发这个事件
3、ontimeout:当 ajax 请求超时,会触发这个事件
*/

JS 中常用的编码解码方法

正常的编码解码(非加密)

1、escape / unescape:主要是把中文汉字进行编码和解码(一般只有JS语言支持,经常应用于前端页面通信时候的中文汉字编码)

2、encodeURI / decodeURI:基本上所有的编程语言都支持

3、encodeURIComponent / decodeURIComponent:和第二中方式非常类似,区别在于 encodeURIComponent 会把一些保留字符(例如: : / 等)也进行编码处理

也可以通过加密的方法进行编码解码

1、可逆转加密(一般都是团队自己玩的规则)

2、不可逆转加密(一般都是基于 MD5 加密完成的,可能会把 MD5 加密后的结果进行二次加密)(引入 md5 的js文件,使用 hex_md5(str) 对字符串进行加密;md5 加密的数据是不可解密的)

AJAX中的同步和异步

AJAX 这个任务:发送请求接收到响应主体内容(完成一个完整的 HTTP 事务)

xhr.send():任务开始

xhr.readState===4:任务结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let xhr = new XMLHttpRequest();
xhr.open('get', 'url', false);
xhr.onreadystatechange = () => {
console.log(xhr.readyState);
};
xhr.send();
// 只输出一次结果是 4


let xhr = new XMLHttpRequest();
xhr.open('get', 'url', false);
xhr.send();
// [同步] 开始发送ajax请求,开启ajax任务,在任务没有完成之前,什么事情都做不了(下面绑定事件也做不了);当readyState===4的时候ajax任务完成,开始执行下面的操作
xhr.onreadystatechange = () => {
console.log(xhr.readyState);
};
// 绑定方法之前状态已经为 4 了,此时ajax的状态不会再改变为其他值了,所以事件不会被触发,一次都没执行方法(使用ajax同步编程,不要把send放在事件监听前,这样我们无法在绑定的方法中获取到响应主体内容)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let xhr = new XMLHttpRequest();
xhr.open('get', 'url');
xhr.onreadystatechange = () => {
console.log(xhr.readyState);
};
xhr.send();
// 输出3次,结果分别是 2 3 4


let xhr = new XMLHttpRequest();
xhr.open('get', 'url');
xhr.send();
xhr.onreadystatechange = () => {
console.log(xhr.readyState);
};
// 2 3 4

1552973955833

AJAX类库的封装

JQ 中的 ajax 使用及每一个配置的作用(包含里面的一些细节知识点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$.ajax({
url: 'xxx', // 请求 API 地址
method: 'get', // 请求方式 GET/POST…… 在老版本 JQ 中使用的是 type,使用 type 和 method 实现的是相同的效果
dataType: 'json', // dataType 只是我们预设获取结果的类型,不会影响服务器的返回,如果预设的是 json你,那么类库中将把服务器返回的字符串转换为 json 对象;如果预设的是 text(默认值),我们把服务器获取的结果直接拿过来操作即可;预设值还可以是 xml 等
cache: false, // 设置是否清楚缓存,只对 GET 系列请求有作用,默认值是 true 不清缓存,手动设置为 false,JQ类库会在请求 URL 的末尾追加一个随机数来清楚缓存
data: null, // 我们通过 data 可以把一些信息传递给服务器:GET 系列请求会把 data 中的内容拼接在 URL 的末尾通过问号传参方式传递给服务器,POST 系列请求会把内容放在请求主体中传递给服务器; data 的值可以设置为两种格式:字符串、对象,如果是字符串,设置的值是什么传递给服务器的就是什么,如果设置的是对象,JQ会把对象变为 xxx=xxx&xxx=xxx 这样的字符串传递给服务器
async: true, // 设置同步或者异步,默认是 true,代表异步
success: function(result){
// 当 ajax 请求成功 (readyState === 4 & status 是以2或者3开头的)
// 请求成功后 JQ 会把传递的回调函数执行,并且把获取的结果当做实参传递给回调函数(result 就是我们从服务器获取的结果)
},
error: function(msg){
// 请求错误触发回调函数
},
complate:function(){
// 不管请求时错误的还是正确的都会触发回调函数(它是完成的意思)
}

// ...
})

封装自己的 AJAX 类库

【支持的参数】:url、method / type、data、dataType、async、cache、success

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
~function () {
class ajaxClass {
// send ajax
init(){
// this: example
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (/^[23]\d{2}$/.test(xhr.status)) return;
if (xhr.readyState === 4) {
let result = xhr.responseText;
// dataType处理
try {
switch (this.dataType.toUpperCase()) {
case 'TEXT':
case 'HTML':
result = result;
break;
case 'JSON':
result = JSON.parse(result);
break;
case 'XML':
result = xhr.responseXML;
}
} catch (e) {

}

this.success(result);
}
};

// data 处理
if (this.data !== null) {
this.formatData();
if (this.isGET) {
this.url += this.querySymbol() + this.data;
this.data = null;
}
}

// cache 处理
this.isGET ? this.cacheFn() : null;
xhr.open(this.method, this.url, this.async);
xhr.send(this.data);
};

// 把传递的对象格式 data 转换为字符串格式 data
formatData() {
if(({}).toString.call(this.data)==='[object Object]') {
let obj = this.data,
str = ``;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
str += `${key}=${obj[key]}&`;
}
}
str = str.replace(/&$/g, '');
this.data = str;
}
}
cacheFn () {
!this.cache ? this.url += `${this.querySymbol()}_=${Math.random()}` : null;
}
querySymbol () {
return this.url.indexOf('?') > -1 ? '&' : '?';
}
}

// init parameters 参数初始化
window.ajax = function ({
url=null,
method='GET',
type=null,
data=null,
dataType='JSON',
cache=true,
async=true,
success=null}={}) {

let example = new ajaxClass();
example.url = url;
example.method = typr === null ? method : type;
example.data = data;
example.dataType = dataType;
example.cache = cache;
example.async = async;
example.success = typeof success === 'function' ? success : new Function();

// 验证是否是 GET 类型请求
example.isGET = /^(GET|DELETE|HEAD)$/i.test(example.method);

example.init();
return example;
};
}();
ajax({});
打赏功能
-------------本文结束感谢您的阅读-------------
0%