0 AJAX基础简介

0.1 什么是AJAX?

async javascript and xml
异步的JS和XML

0.2. xml可扩展的标记语言

作用是用来存储数据的(通过自己扩展的标记名称清晰地展示出数据结构)

ajax之所以成为异步的js和xml,主要原因是:当初最开始使用ajax实现客户端和服务端数据通信的时候,传输数据格式一般都是xml格式的数据,我们把它称之为异步js和xml(现在一般都是基于JSON格式来进行数据传输的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<student>
<name>张三</name>
<age>25</age>
<score>
<english>90</english>
<math>100</math>
<chinese>97</chinese>
</score>
</student>
<student>
<name>张三</name>
<age>25</age>
<score>
<english>90</english>
<math>100</math>
<chinese>97</chinese>
</score>
</student>
</root>

0.3 异步的JS

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

1 前后端分离

前后端分离的导火线就是全局刷新和局部刷新

1.1刷新全局(不分离)

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

  1. 动态战士的数据在页面的源代码中课件,有利于SEO优化推广(有利于搜索引擎的收录和抓取)

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


[弊端]

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

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

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


目前市场上大部分项目都是前后端完全分离的项目(也有非完全前后端分离的项目)

1.2 局部刷新(分离)

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

  1. 向服务器发送AJAX请求
  2. 把从服务器端获取的数据解析进行处理,拼接成我们需要的HTML字符串
  3. 把拼接好的字符串替换页面中某一部分的内容(局部刷新),页面整体不需要重新加载,局部渲染即可

[优势]

  1. 我们可以根据需求,任意修改页面中某一部分的内容(例如实时刷新),整体页面不刷新,性能好,体验好(所有表单验证,需要实施刷新的需求都要局域AJAX实现)
  2. 有利于开发,提高开发效率
    1) 前后端分离,后端不需要考虑前端是如何呈现的,前端也不需要考虑后台使用什么技术,真正意义上实现了技术划分
    2) 可以同时进行开发:项目开发开始,首先指定前后端数据交互的接口文档(文档中包含了,调取那个接口或者那些数据等协议规范),后台把接口写好(目前很多公司也需要前端自己拿NODE来模拟这些接口)

[弊端]

  1. 不利于SEO优化:第一次从服务器端获取的内容不包含需要动态绑定的数据,所以页面的源代码中没有这些内容,不利于SEO收录,后期经过JS添加到页面中的内容,并不会卸载页面的源代码中(是源代码不是页面结构)
  2. 交由客户端进行渲染,首先需要把页面呈现,然后再通过JS的异步AJAX请求获取数据,然后数据绑定,浏览器把动态数据增加的部分重新渲染,无形中兰妃了一些时间,没有服务器端渲染页面呈现的速度快

如图所示

2 AJAX参数详解

2.1 创建一个AJAX对象

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

//打开请求地址(可理解为一些基础配置,但是并没有发送请求)
xhr.open([method],[url],[async],[user_name],[user_password]);
// 监听AJAX状态改变,获取相应信息(获取响应头信息,获取响应主体信息)
xhr.onreadystatechange=()=>{
if(xhr.readyState === 4 && xhr.status === 200){
let result = xhr.responseText;//获取相应主题中的内容
}
}
// 发送AJAX请求(括号中的内容就是请求的主题内容)
xhr.send(null);

2.2 配置(open)的参数

xhr.open([method],[url],[async],[user_name],[user_password]);
1. [method]请求方式
1) GET系列的请求

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

  • 2) POST请求方式
  • post
  • put 向服务器中增减指定的资源文件

不管是哪一种请求方式,客户端都可以把信息传递给服务器,服务器也可以把信息返回给客户端,只是GET系列一般以获取为主(给的少,拿得多)而POST系列一般以推送为主(给的多,拿得少)
1) 我们想获取一些动态展示的信息一般都用GET请求,因为只需要向服务器发送请求,告诉服务器端我们想要什么
2) 在实现注册功能的时候,我们需要吧客户端输入的信息发送给服务器进行存储,服务器一般返回成功还是失败等状态,此时我们一般都是基于POST请求完成的

2. GET VS POST
GET系列请求和POST系列请求系列请求,在项目实战中存在很多区别
1) GET请求传递给服务器的内容一般没有POST请求传递给服务器的多
原因:GET请求传给服务器内容一般都是基于url地址问号传参来实现的,而POST请求一般都是基于设置请求主体来实现的。各浏览器都有自己的关于URL最大长度限制(谷歌:8K,火狐:7k,IE:2K)超过限制长度的部分,浏览器会自动截取掉,导致传递给服务器的数据缺失。理论上POST请求通过主体传递是没有大小限制的,真实项目中为了保证传输的速率,我们也会限制大小(例如:上传的资料或者图片我们会做大小的限制)
2) GET请求很容易出现你缓存(这个缓存不可控:一般我们不需要),而POST不会出现缓存(除非做特殊处理)

1
2
3
4
5
6
7
8
9
setTimeout(()=>{
$.ajax({
url:'getList?lx=news',
...
success=>{
//第一次请求数据回来,间隔一分钟后,浏览器又发送一次请求,但是发送新的请求,不管是地址还是传递的参数都和上一次一样,浏览器很有可能会把上一次数据获取,而不是获取最新数据
}
},60000);
})

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

1
2
3
4
5
6
7
8
9
setTimeout(()=>{
$.ajax({
url:'getList?lx=news&_='+Math.random(),
...
success=>{

}
},60000);
})

3) GET请求没有POST请求安全(POST也并不是十分安全,只是相对安全)
原因:还是因为GET是URL传参给服务器
有一种比较简单的黑客技术:URL劫持,也就是可以把客户端传递给服务器的数据劫持掉,导致信息泄露

3. [url]请求地址
真实项目中,后台开发工程师会写一个API文档,在API文档中汇总了获取哪些数据需要使用那些地址,我们按照文档操作即可

4. [async]异步
ASYNC异步(SYNC同步),设置当前AJAX请求时异步还是同步的,不写默认是异步(TRUE),如果设置为FALSE,则代表当前请求时同步

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

2.3 AJAX状态码

描述当前AJAX操作的状态的xhr.readyState

0 :UNSENT 未发送,只要创建一个AJAX对象,默认是0

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

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

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

2.4 HTTP网络状态

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

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

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

301:Moved Permanently 永久颛臾(永久重定向)

302:Moved temporarily 临时转移(临时重定向,新的HTTP版本中任务307是临时重定向)
一般用于服务器负载均衡:当前服务器处理不了,我们把当前请求临时交给其他的服务器处理(一般图片请求经常出现302,很多公司都有单独的图片服务器)

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

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

400:请求参数错误

401:无权限访问

404:访问地址不存在

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

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

503:Service Unavailable服务器负载

2.5 AJAX参数及方法

运行下面代码查看AJAX属性及方法

1
2
let xhr = new XMLHttpRequest();
dir(xhr)

  1. [属性]

readState

存储的是当前AJAX的状态码

response/responseText/responseXML

都是用来接受服务器返回的响应主体中的内容,只是根据服务器返回内容格式不一样,我们使用不同的属性接收即可

responseText是最常用的,接收到的内容格式是字符串格式的(一般服务器返回的数据都是JSON格式字符串)

responseXML偶尔会用到,如果服务器端返回的是XML文档数据,我们需要使用这个属性接收

starus

记录了服务器返回的HTTP状态码

statusText

返回状态码的描述

timeout

设置当前AJAX请求的超时时间,假设我们设置的时间为3000(MS),从AJAX请求发送开始,3秒后响应主体内容还没有返回,浏览器就会把当前的AJAX请求任务强制断开

  1. [方法]

abort()

强制中断AJAX请求

getAllResponseHeaders()

获取指定属性名的响应头信息,例如:xhr.getResponseHeader(‘date’)获取响应头中存储的服务器时间

open()

打开一个URL地址

overrideMimeType()

重写数据的MIME类型

send()

发送AJAX请求(括号中书写的内容是客户端基于请求主体把信息传递给服务器)

setRequesHeader(key,value)

设置请求头信息(可以是设置的自定义请求头)

  1. [事件]

onabort

当AJAX状态发生改变,会触发这个事件

onreadystatechange

AJAX状态发生改变,会触发这个事件

ontimeout

当AJAX请求超时,会触发这个事件

  1. [代码示例]
    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
    let xhr = new XMLHttpRequest();
    xhr.open('get','url?_='+Math.random(),true);
    // xhr.setRequestHeader('cookie','培训')设置请求头内容不能出现中文,并且header的key不能使cookie
    //请求头必须在OPEN之后SEND之前
    xhr.setRequestHeader('aaa','xxx');
    //设置请求超时时间
    xhr.timeout = 10;
    xhr.ontimeout = ()=>{
    console.log('请求已超时');
    xhr.abort();
    }
    xhr.onreadystatechange=()=>{
    let {readyState:state,status}=xhr
    // 如果跳过这个if没有return则说明成功了
    if(!/^(2|3)\d{2}$)/.test(status)) return;

    // 在状态为2的时候就可以获取响应头信息
    if (state === 2){
    let headerAll = xhr.getAllResponseHeaders(),
    //获取的是服务器时间是格林尼治时间(相比于北京时间差了8小时),通过new Date可以把这个时间转换为北京时间
    serverDate = xhr.getResponseHeader('data');
    console.log(headerAll,new Date(serverDate));
    return;
    }
    // 在状态为4的时候响应主体已经回来了
    if (state === 4){
    //获取的结果一般是JSON字符串(可以使用JSON.PARSE把其转换为JSON对象)
    let valueText = xhr.responseText,
    //获取到的结果是SML格式的数据(可以通过XML的一些常规操作获取存储的信息)
    //如果服务器返回的是XML文档,responseText获取的结果是字符串而responseXML获取的是标准XML文档
    valueXML = xhr.responseXML;
    console.log(valueText,valueXML);
    return;
    }

    }
    xhr.send('name=zxt&age=28&sex=man');

2.6 AJAX同步异步详解

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

xht.send() 开始任务

xht.readyState === 4; 结束任务

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

原理如图所示:如图所示

  1. [异步]
    1
    2
    3
    4
    5
    6
    7
    let xhr = new XMLHttpRequest();
    xhr.open('get','temp.json');
    xhr.onreadystatechange = ()=>{
    console.log(xhr.readyState);
    }
    xhr.send();
    // 输出结果分别是2,3,4
1
2
3
4
5
6
7
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = ()=>{
console.log(xhr.readyState);
}
xhr.open('get','temp.json');
xhr.send();
// 输出结果分别是1,2,3,4

原理如图所示:如图所示

  1. [AJAX特殊性]

虽然是同步但是还是会输出1

1
2
3
4
5
6
7
8
9
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = ()=>{
console.log(xhr.readyState);
}
//xhr.readyState === 1 AJAX特殊处理的一件事:执行OPEN状态改变为1,会主动把之前坚挺的方法执行一次,然后再去执行SEND
xhr.open('get','temp.json',false);
xhr.send();
//xhr.readyState ===4 AJAX任务结束,主任务队列完成
// 1,4

3 JS中常用的编(解)码方法

3.1 正常编码解码

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

    1
    2
    3
    4
    5
    let str = '培训@163.com'
    escape(str);
    // 显示"%u57F9%u8BAD@163.com"
    unescape('%u57F9%u8BAD@163.com')
    // 显示"培训@163.com"
  2. encodeURI/decodeURI

    1
    2
    3
    4
    5
    let str = '培训@163.com';
    encodeURI(str);
    // "%E5%9F%B9%E8%AE%AD@163.com"
    decodeURI('%E5%9F%B9%E8%AE%AD@163.com')
    // "培训@163.com"
  3. encodeURIComponent/encodeURIComponent

    1
    2
    3
    4
    5
    let str = '培训@163.com';
    encodeURIComponent(str);
    // "%E5%9F%B9%E8%AE%AD%40163.com"
    encodeURIComponent('%E5%9F%B9%E8%AE%AD%40163.com');
    // "培训@163.com"
  4. encodeURI区别encodeURIComponent
    1) encodeURI不处理特殊字符
    2) encodeURIComponent处理特殊字符
    我们URL问号传递参数的时候,我们传递的参数还是一个URL或者包含很多特殊字符,此时为了不影响主要的URL,我们需要吧传递的参数值进行编码,使用encodeURI不能编码一些特殊字符,所以只能使用encodeURIComponent处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let str = 'http://www.baidu.com?',
    obj={
    name:'培训',
    age:'9',
    url:'http://www.linjiad.cn/?lx=1'
    };
    //把obj中的每一项属性名和属性值拼接到url末尾
    for(let key in obj){
    str+=`${key}=${obj[key]}&`;
    }
    console.log(str.replace(/&$/g,''));
    //后期获取URL问号传参的时候,我们把获取的值一次的解码即可

3.2 加密方式进行编码解码

  1. 可逆转加密(一般都是团队自己定义的规则)
  2. 不可逆加密(一般都是基于MD5加密完成的)
    MD5加密是不可逆的,网上解析是通过匹配大数据,能解析出常用的加密,不常用的是无法解析的

4 实现AJAX

4.1 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
$.ajax({
url:'xxxx', // 请求的API地址
method:'get',// 请求方式GET/POST。。,在老的版本中使用的是type使用type和method实现的是相同效果
dataType:'json',// dataType知识我们预设获取结果的类型,不会影响服务器的返回
// (服务器一般给我们返回的都是JSON格式字符串),如果我们预设的是json,那么类库中将把服务器返回的字符串转换为json对象
// ,如果我们预设的事text(默认值),我们把服务器获取的结果直接拿过来即可,我们预设的值还可以是xml等
cache:false, // 设置是否清除缓存,只对GET系列请求有作用,默认是TRUE不清除缓存,手动设置为FALSE,JQ类库
// 会在请求URL的末尾追加一个随机数来清除缓存
data:null, //我们通过DATA可以吧一些信息传递给服务器;GET系列请求会把DAT中的内容拼接在URL的末尾,
// 通过问号传参的方式传递给服务器,POST系列请求会把内容放在请求主体中传递给服务器,DATA的值可以设置为两种格式
//字符串,对象,如果是字符串,设置的值是什么传递给服务器的就是什么,如果设置的对象,
// JQ会把对象变为xxx=xxx&xxx=xxx这样的字符串传递给服务器
async:true,// 设置同步或者异步,默认是TRUE代表异步,FALSE是同步
success:function() {
// 当AJAX请求成功(readyState===4&status是2或3开头的)
// 请求成功后JQ会把传递的回调函数执行,并且把获取的结果当做实参传递给回调函数(result就是我们从服务器获取的结果)
},
error:function() {
// 请求错误触发回调函数
},
complate:function() {
// 不管是否请求错误还是正确,都会触发回调函数(他是完成)
}
})

4.2 封装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
~function() {
class ajaxClass{
init(){
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () =>{
if(!/^[23]\d{2}$/.test(xhr.status)) return;
if(xhr.readyState === 4){
let result = xhr.responseText;
switch (this.dataType.toUpperCase()){
case 'TEXT':
case 'HTML':
break;
case 'JSON':
result = JSON.parse(result);
case 'XML':
result = xhr.responseXML;
}
this.success(result);
}
};
// 处理data
if(this.data !== null){
this.formatData();
if(this.isGet){
this.url += `${this.querySymbol()}`;
}
}
// 是否去除缓存
this.isGet ? this.cacheFn():null;
xhr.open(this.method,this.url,this.async);
xhr.send(this.data);
}
formatData(){
if(Object.prototype.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?'&':'?';
}

}
window.ajax = function({
url = null,
method = 'GET',
type = 'GET',
data = null,
dataType = 'JSON',
cache = true,
async = true,
success = null
} = {}){
let example = new ajaxClass();
example.url = url;
example.method = type === null?method:type;
example.data = data;
example.dataType = dataType;
example.cache = cache;
example.async = async;
example.success = typeof success === 'function'?success: new Function();
example.isGet = /^(GET|DELETE|HEAD)$/i.test(example.method);
example.init();
}

}();

最后更新: 2018年12月01日 08:23

原始链接: http://linjiad.github.io/2018/11/23/AJAX/

× 请我吃糖~
打赏二维码