AJAX核心知识总结
XMLHttpRequest 对象 - JavaScript 教程 - 网道 (wangdoc.com)
2005年2月,AJAX 这个词第一次正式提出,它是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。后来,AJAX 这个词就成为 JavaScript 脚本发起 HTTP 通信的代名词,也就是说,只要用脚本发起通信,就可以叫做 AJAX 通信。W3C 也在2006年发布了它的国际标准。
AJAX有独特的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。
使用AJAX的四步过程如下:
let xhr = new XMLHttpRequest;//1
xhr.open( 'GET', './1.json');//2
xhr.onreadystatechange = function () {//3
if (xhr. readyState === 4 && xhr.status === 200) {
console. log( xhr.responseText);
}
)
xhr.send();//4
分别是:
- 创建xhr实例
- 使用
open()
方法指定建立 HTTP 连接的一些细节。在发送请求前作一些配置 - 指定回调函数,监听通信状态的变化
- 使用
send()
方法实际发出请求
下面分别详细展开说一下这四步
创建xhr实例
let xhr = new XMLHttpRequest
使用 open()
在发送请求前作一些配置
...
xhr.open( 'GET', './1.json');//2
...
open
方法第一个参数 [method]
是请求方式。包括 GET(get/delete/head/options...) / POST(post/put/patch...)。
GET与POST的区别
GET和POST在官方定义中是没有明确的区别的,但是浏览器或者开发的时候,都有一套约定俗成的规范:
- GET请求传递给服务器的信息,除了请求头传递以外,要求基于URL问号传参传递给服务器。例如:
xhr.open('GET', './1.json?lx=1&name=xxx')
- POST请求要求传递给服务器的信息,是基于请求主体传递
xhr.send('lx=1&name=xxx')
这两个本质的不同造成了以下的不同:
-
GET传递的信息不如POST多,因为URL有长度限制(IE->2KB),超过这个长度的信息会被自动截掉,这样导致传递内容过多,最后服务器收到的信息是不完整的。POST理论上是没有限制的,但是传递的东西越多,速度越慢,可能导致浏览器报传输超时的错误,所以实际上我们会自己手动做限制
-
GET会产生缓存,这个缓存是浏览器默认产生的,不可控的。如果两次及以上,请求相同的API接口,并且传递的参数也一样,浏览器可能会把第一次请求的信息直接返回,而不是从服务器获取最新的信息。
可以在请求URL的末尾设置随机数,以此来清除GET缓存的副作用:
xhr.open('GET', './1.json?lx=1&name=xxx&_'+Math.random())
-
POST相对于GET来讲更安全一些:GET传递的信息是基于URL末尾拼接,劫持和修改比较容易,而POST请求主体信息的劫持,没有GET容易。
open
方法第二个参数 [url]
是 请求的URL地址,第三个参数 [async]
是否采用异步,默认是 true
监听请求的过程
监听请求的过程,在不同的阶段做不同的处理,包含获取服务器的响应信息,
-
ajax状态
xhr.readyState
- 0 UNSENT
- 1 OPENED
- 2 HEADERS_RECEIVED 响应头信息已经返回
- 3 LOADING 响应主体信息正在处理
- 4 DONE 响应主体信息已经返回
-
HTTP状态码
xhr.status
/xhr.statusText
- 200 OK
- 202 Accepted 服务器已接受请求,但尚未处理(异步)
- 204 No Content服务器成功处理了请求,但不需要返回任何实体内容
- 206 Partial Content 服务器已经成功处理了部分 GET 请求(断点续传 Range/If-Range/Content-Range/Content-Type:”multipart/byteranges”/Content-Length…。)
- 301 Moved Permanently 永久转移 (域名迁移)
- 302 Move Temporarily 临时转移 (负载均衡)
- 304 Not Modified
- 305 Use Proxy
- 400 Bad Request : 请求参数有误
- 401 Unauthorized:权限(Authorization)
- 403 Forbidden 服务器拒绝执行(可能会已响应主体返回)
- 404 Not Found 地址错误
- 405 Method Not Allowed 请求方式不被允许
- 408 Request Timeout 请求超时
- 500 Internal Server Error 未知服务器错误
- 503 Service Unavailable 超负荷
- 505 HTTP Version Not Supported
- ......
-
获取响应主体信息
方法:
xhr.response
/xhr.responseText
/xhr.responseXML
服务器返回的响应主体信息的格式如下:
- 字符串。一般是JSON字符串最常用
- XML格式数据
- 文件流格式数据(buffer/二进制...)
- ...
-
获取响应头信息
xhr.getResponseHeader
/xhr.getAllResponseHeaders
使用 send()
方法发送请求请求
发送请求时,send()
方法中传入的信息,就是请求主体信息
在发送之前,可以设置请求头,告诉后端传送的是什么样的数据格式等信息。请求头必须设置在 xhr.open()
之后, xhr.send()
之前,且不能设置为中文,设置方法如下:
xhr.setRequestHeader('Content-Type', 'application/JSON')
基于请求主体传递给服务器的数据格式是有要求的,几种数据格式分类如下:
-
form-data格式:主要应用于文件的上传或者表单数据提交
xhr.setRequestHeader('Content-Type', 'multipart/form-data'); //手动设置一个表单数据: let fd = new FormData; fd.append('lx', 0); fd.append('name', 'xxx'); xhr.send(fd);
-
x-www-form-urlencoded格式的字符串
格式为
'lx=1&name=xxx'
,这种方式比较常用。可以安装qs库并使用Qs.stringify/parse
实现对象和urlencoded格式字符串之间的转换。例如:xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(Qs.stringify({ lx: 0, name: 'xxx' }));
-
raw格式字符串:
raw格式分为以下几种
-
普通字符串 -> text/plain
-
JSON字符串 -> application/json 可以使用
JSON.stringify/parse
对json进行解析,这种方式也常用 -
XML格式字符串 -> application/xml
-
...
例如 模拟javascript的raw格式:
如果什么请求头都不设置的话默认是按照
'text/plain'
类型传送的,并且会把请求体中的内容转化为字符串,例如: -
let xhr = new XMLHttpRequest;
xhr.open('POST', './data.json');
xhr.onreadystatechange = function () {
if (xhr.status=== 200&& xhr.readyState === 4) {
console.log(xhr.response);
}
};
xhr.send({name:'xxx'});
可以看到发送的对象请求体被自动转化成了字符串
-
binary进制数据文件(buffer/二进制等),一般也应用于文件上传
例如图片 -> image/jpeg,用postman演示如下:
-
GraphQL
开发时在传送不同类型的数据的时候,需要自己设置请求头。和后台协商好需要什么样的格式后,统一请求头。
最后:xhr还有一些其他的方法
例如 :
xhr.abort()
中断请求,执行此方法会触发xhr.onabort
方法xhr.timeout=0
设置超时时间,超时后可以可以触发xhr.ontimeout
方法xhr.withCredentials=true
在CORS跨域资源请求中允许携带资源凭证,例如cookiexhr.upload.onprogress
原生监听文件上传的进度的方法
注意:设置请求头信息/超时时间或设置可以携带资源凭证都需要在 xhr.open()
之后, xhr.send()
之前
额外tip:
两种API请求方式
xhr.open('GET', '/userInfo?id=1'); //=>router Query
xhr.open('GET', '/userInfo/1'); //=>router Params
这样请求后端会这样处理: app.get('/userInfo/:id')
表单提交总结
axios核心基础总结
axios是基于Promise封装的ajax库,核心原理还是XMLHttpRequest
一些属性
axios
本身是函数(对象),有以下常用属性:
CancelToken
用于取消ajax请求all
基于promise.all
实现ajax的并行,当所有的ajax请求都成功,整体才会返回一个成功的promise实例spread
解析出基于all
返回的结果create
创建一个新的实例,来做单独的全局配置defaults
全局默认配置get
/delete
/head
/options
发送对应方式的请求post
/put
/patch
发送对应方式的请求request
发送请求interceptors
request
请求拦截器response
响应拦截器
一些配置
基于axios发送请求,最后返回的都是promise实例。
一下常用属性以及作用注释如下:
axios({
// baseURL+url:最终请求的地址
baseURL: 'http://127.0.0.1:8888',
url: '/user/list',
method: 'post',
// params:基于URL末尾拼接参数的方式,把params对象一项项传递给服务器
params: {
lx: 0,
from: 'wx'
},
// data:只针对POST系列请求,设置请求主体传递的信息,默认会把对象变为 application/json 字符串传递给服务器
data: {
file: 'xxx',
size: 1024
},
// 在POST请求下,把请求主体信息发送给服务器之前,对请求主体信息进行处理
transformRequest: function (data) {
return Qs.stringify(data);
}
// 值:FormData\binary\raw...
// 设置请求头信息
headers: {
// 所有请求通用,以下两种方法用于设置公共请求头
'Content-Type': 'multipart/form-data',
common: {
'X-Token': 'xxx'
},
// 可以只针对某种请求设置
post: {
'lx': 1
},
get: {
'lx': 0
}
},
// 零散配置信息
timeout: 0,
withCredentials: true,
// 预设服务器返回的数据格式:不论服务器返回啥格式,内部会转换为我们预设的格式 json/arraybuffer/blob/document/text...
responseType: 'json',
// 监听上传/下载进度
onUploadProgress: function (progressEvent) {},
onDownloadProgress: function () {},
// 内部规定,HTTP状态码为多少,算是请求成功,返回成功Promise,否则返回失败的!!
validateStatus: function (status) {
return status >= 200 && status < 300;
}
});
data
和 transformRequest
细节
默认会把对象变为 application/json 字符串传递给服务器,
...
data: {
file: 'xxx',
size: 1024
},
// 在POST请求下,把请求主体信息发送给服务器之前,对请求主体信息进行处理
transformRequest: function (data) {
return Qs.stringify(data);
}
...
也可以使用 transformRequest
进行处理,处理完之后,Headers里的Content-Type会自动改变,例如这里就改变成了x-www-form-urlencoded
data
也可以直接支持其他格式的数据,例如formData格式的数据
let formData = new FormData();
formData.append('file', 'xxx');
formData.append('size', '1024');
axios({
...
data: formData,//data可以直接支持formData格式的数据
transformRequest: function (data) {
if (_.isPlainObject(data)) {
//这样可以排除formData,new ArrayBuffer()等形式的数据
// application/json && x-www-form-urlencoded
return Qs.stringify(data);
}
return data;
},
...
})
发送请求的几种方式
axios([config])
axios.request([config])
axios.get/head/delete/options([url],[config])
/axios.post/put/patch([url],[data],[config])
返回结果
返回结果一定是Promise,一些回调函数中返回结果的属性如下:
axios.get('http://127.0.0.1:8888/user/list2', {
params: {
lx: 1,
from: 'wx'
}
}).then(response => {
// 服务器返回的状态码和validateStatus指定的匹配条件一致(READY-STATE===4)
//有以下几个属性:
// config 设定的配置项
// headers 响应头信息「对象」
// request 原生的XHR对象
// status/statusText 状态码和状态码的描述
// data 响应主体信息(body)
console.log('成功', response);
return response.data;
}).then(data => {
// 获取响应主体信息,完成对应的业务逻辑
}).catch(reason => {
//有几种原因
// 1. 服务器返回的状态码不与validateStatus条件一致「最起码服务器有返回」
// 2. 压根服务器啥都没返回「例如:断网」
// 3. 当前请求超时或者被取消
//reason也有几个属性:
// + config
// + request
// + toJSON
// + message 错误信息
// + response 如果是网络层失败,是没有response,如果只是axios层失败,是存在response的
// + isAxiosError 是否为axios层面失败
console.dir(reason);
});
axios请求失败的几种原因分类(都会走到 catch
):
- 网络层失败。请求没有发送成功,或者没有任何的响应(没有完成一个HTTP事务)
- axios层面失败
- 服务器一定有返回,只不过状态码和validateStatus不一致
- 超时或者取消请求
- 业务层失败。一般都是服务器根据业务需求,基于类似于code等标志,来区分不同的业务形态和结果
axios请求举例
POST是基于请求主体把信息传递给服务器,对数据格式有要求,需要在请求头中修改数据格式
请求头: 'Content-Type{MIME}'
,几个不同type的例子:
-
json格式字符串: type 为
'application/json'
,例如'{"account" :"18310612838"...}'
,当axios发送post请求并在请求体中传入一个对象时,默认会把这个对象转化为json格式字符串 -
urlencoded格式字符串:
'application/x-www-form-urlencoded '
例如:'account=xxx&password=xxx '
-
form-data格式,
'multipart/ form-data'
,文件上传就可以使用这种格式,把文件对象(文件流信息)传递给服务器
urlencoded格式上传
axios.post('http://127.0.0.1:9999/user/login', {
// data
account: '13711111111',
password: md5('1234567890')
}, {
// POST系列请求下:把传递进来的DATA对象,变为自己想要的格式
transformRequest: (data, headers) => {
// Qs.stringify/parse实现urlencoded格式字符串和对象之间的转换
return Qs.stringify(data);
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(response => {
console.log(response.data);
})
上传文件
文件上传举例:
//选择一个图片,自动上传
fileInp.onchange = function () {
let file = fileInp.files[0];
if (!file) return;
let fm = new FormData;
fm.append('file', file);
axios.post('http://127.0.0.1:9999/upload', fm);
}
发送请求的结果:
axios发现是form-data格式的数据,会自动将Content-Type改为'multipart/ form-data'