AJAX
引入:你是如何理解AJAX的?
AJAX(Asynchronous Javascript And XML,异步JS和XML)。所谓异步JS指的是基于AJAX进行局部刷新。(全局刷新 vs 局部刷新)
服务器渲染时代(全局刷新):服务器收到HTTP请求,获取到请求的资源文件页面中的代码,从数据库中根据业务逻辑获取对应数据,混合到一起渲染。通过HTTP响应返回给客户端,浏览器只需要直接渲染就好了。服务器端要做的事情超多的,而且如果页面中某个部分数据需要动态更新,需要向服务器重新发请求,页面整体刷新一次。
客户端渲染时代(局部刷新):向服务器请求返回的只有结构index.html,某个部分需要更新时,再发一个AJAX请求拿到数据,通过JS动态填充到结构中。页面整体不刷新。但有一点不好,所有基于客户端渲染的内容,都不会在源代码中,这样也就不会被搜索引擎收录(搜不到),不利于SEO推广。
AJAX核心四步操作
- 创建一个XMLHttpRequest实例对象。
- 打开请求连接,配置请求信息。
- 监听请求状态——不同的状态做不同的事。
- 发送AJAX请求,AJAX任务开始,一直到响应主体信息返回代表任务结束。
XMLHttpRequest实例对象
let xhr = new XMLHttpRequest; // 创建XMLHttpRequest实例对象
IE低版本浏览器中,使用new ActiveXObject
创建对象。
想要扒一扒这个对象里都有啥属性 / 方法可以在浏览器控制台输出一下哦~
打开请求连接
/* @params:
* method:请求方法类型
* url:请求的文件在服务器上的位置
* async:true(异步)或 false(同步),一般都为默认值true
*/
xhr.open(method,url,async);
注意,open方法只是打开请求连接,并没有发送请求。
请求方法
GET系列 vs POST系列
GET与POST系列请求都可以在客户端和服务器之间往返,只不过在实际使用中,GET系列请求更倾向于从服务器获取(索取型人格),而POST系列请求更倾向于向服务器发送(付出型人格)。
GET系列请求包含:GET、HEAD(只获取响应头)、DELETE(一般指删除服务器上指定文件)、OPTIONS(试探性请求,常用于跨域请求)
POST系列请求包含:POST、PUT(与DELETE相对,向服务器新增文件)
基于两种方式向服务器发送请求,传递给服务器的方式:
① 都可以基于请求头发送。
② GET请求的主要方式是<mark style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">通过URL地址后的问号传参</mark>。
③ POST请求的需要方式是<mark style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">基于请求主体发送</mark>。
正是由于这个区别,诞生了GET和POST系列的重要差异:
- GET传给服务器的信息有大小限制——毕竟写在URL里,太长了会被自动截掉(相比大家都有这种体会);POST理论上是没有大小限制的,但实际项目中一般会手动进行限制,传的东西太笨重当然要尽量避免,会造成服务器压力过大,降低速度。
- GET请求相对于POST不太安全,地址栏问号传参有被劫持的风险。但互联网中没有绝对的安全,涉及到对安全要求高的请求最好进行加密。
- GET请求容易产生缓存(浏览器默认的),这个缓存我们经常是不想要的。若需要实时数据刷新,就不能走缓存。一般会在地址栏传参时添加一个随机数或时间戳,这样地址和参数不会完全一样就不会走缓存啦~
&_ = Math.random()
。用_
接收这个随机参数服务器会忽略掉。
举几个GET和POST请求的例子:
// get
let xhr = new XMLHttpRequest;
xhr.open('get','./data.json?lx=1&name=cmj');
xhr.send();
// post
let xhr = new XMLHttpRequest;
xhr.open('post','./data.json');
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('lx=1&name=cmj');
// post请求的另一种类型
xhr.setRequestHeader('Content-Type','multipart/form-data');
let formData = new FormData();
formData.append('lx',2);
formData.append('name','szr');
xhr.send(formData);
说到这就有必要好好说一下xhr对象的setRequestHeader这个方法。
为啥要用到这个方法?通常在HTTP协议里,客户端向服务器取得某个网页的时候,必须发送一个HTTP协议的头文件,告诉服务器客户端要下载什么信息以及相关的参数。但默认的情况下有些参数可能没有说明在HTTP头里,当我们需要修改或添加这些参数时就用到了setRequestHeader 方法。
此方法必须在 open() 方法和 send() 之间调用。
/* @params:名值对 */
xhr.setRequestHeader(str,value);
get请求可以不用设置请求头,而post请求必须设置请求头。
那我们一般都会设置哪些请求头呢?最常用的就是Content-Type数据格式
。
传递给服务器的数据格式有:
Content-Type | Description |
---|---|
application/x-www-form-urlencoded | 最常用的方式,即xxx=xxx&xxx=xxx ,在问号传参和post请求中一般都要求使用这种方式,结果是字符串类型 |
multipart/form-data | 表单提交或文件上传时常用,结果是一个对象,参数以对象名值对的方式传递 |
raw | text,json,xml,html等格式的文本,在富文本编辑器中的内容也可,结果是字符串类型 |
binary | 二进制数据 / 编码格式数据 |
监听请求状态
这一步经常涉及到xhr对象的两个属性readyState
,status
和一个事件onreadystatechange()
。当请求被发送到服务器时,我们需要执行一些基于响应的任务。每当 readyState
改变时,就会触发 onreadystatechange
事件。
AJAX状态码——xhr.readyState
状态码 | 含义 |
---|---|
0 | UNSEND未发送,当创建了一个XMLHttpRequest对象时状态码为0 |
1 | OPENED已打开,xhr.open方法执行结束后状态码更新为1 |
2 | HEADERS_RECEIVED,send请求发出后,接收到响应头信息 |
3 | LOADING,响应主体信息回传中 |
4 | DONE,接收到响应主体信息,AJAX任务结束 |
HTTP状态码——xhr.status
只列举常用的哦,想要了解全部请自行百度,一大堆。
状态码 | 含义 |
---|---|
2xx | 成功,操作被成功接收并处理 |
200 | OK,希望看到的一种状态,但只代表在网络层面上成功接收,至于业务层面上是不是你想要的就不一定了,还需要前后端协商好数据判断标识。 |
3xx | 重定向,需要进一步的操作以完成请求 |
301 | Moved Permanently 永久重定向,比如这个域名不用啦,现在使用新的域名(但是同一个网站),当访问旧域名时就会永久重定向到新的域名下。 |
302 | Move temporarily 临时重定向,一般用于服务器负载均衡 |
304 | Not Modified 未修改。返回此状态码时,不会返回任何资源,而是让浏览器去获取缓存中的资源。 |
307 | Temporary Redirect 临时重定向,与302傻傻分不清楚 |
4xx | 客户端错误 |
400 | Bad Request 客户端请求的语法错误,服务器无法理解 |
401 | Unauthorized 未授权,没权限访问 |
403 | Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求 |
404 | Not Found 您所请求的资源无法找到 |
405 | Method Not Allowed 当前请求方式服务器不支持 |
5xx | 服务器错误 |
500 | Internal Server Error 服务器内部错误 |
503 | Service Unavailable 服务器超负荷,比如春运抢票12306网站崩掉 |
不过在真实项目中,后台可能不按照这个规则处理,有可能都返回OK,然后在返回的JSON中,通过code标识错误信息。
在监听AJAX状态时进行的操作举例:
xhr.onreadystatechange(){
let status = xhr.status,
state = xhr.readyState,
result = null;
if(!/^(2|3)\d{2}/.test(status)){ // 如果返回的不是2xx或3xx开头的状态码,进行一些错误处理
...
return;
}
if(state === 2){ // 响应头信息回来了
console.log(xhr.getAllResponseHeaders()); // 获取所有的响应头信息
console.log(xhr.getResponseHeader('data')); // 获取指定的响应头信息
}
if(state === 4){ // 响应主体信息回来了
result = xhr.response; // 获取响应主体信息
}
}
发送请求
xhr.send();
该方法的参数为请求主体;也就是说,GET请求的send方法不传参或传null,而POST请求才将请求主体作为send方法的参数。
注意:post请求要配合设置请求头。比如我设置了请求头中数据格式为application/x-www-form-urlencoded
,但传递的请求主体又不是xxx=xxx&xxx=xxx
这种字符串格式,它是不会进行自动转换的,我的请求头只是通知一下服务器而已,并没有对请求主体转换格式的能力!
那咋整?必须要进行参数序列化。
可以引入Qs类库,使用Qs.stringify()转换为application/x-www-form-urlencoded
格式,也可以自己封装一个参数序列化的方法嗷。具体来说,就是将{lx:1,name:'cmj'}
转换为lx=1&name=cmj
。