今天被问到用没用过ajax axios,我回答
经常用axios,但ajax用的比较少
,额。。。首先,axios是ajax的promise封装版,跟jquery是ajax的回调函数封装版类似
ajax (async jasvascript And xml)
[r1] Ajax文档 (MDN) 本文大篇幅参考MDN
[r2] AJAX (阮一峰)
1.1 ajax
1)ajax介绍
ajax (async jasvascript And xml)即异步JavaScript和XML,但
尽管X在Ajax中代表XML, 但由于JSON的许多优势,比如更加轻量以及作为Javascript的一部分,目前JSON的使用比XML更加普遍。JSON和XML都被用于在Ajax模型中打包信息。
ajax有很多种方式:1XHR 2Fetch API 3Server-sent事件(使用服务器发送事件)
fetch 和 server-sent,在IE中都不被支持,我们把重点放在XHR上
2)XMLHttpRequest
API是Ajax的核心
首先看看一些常用属性和方法:
名称 | 作用 | |
---|---|---|
onreadystatechange | 该属性指定一个响应的回调 | |
readyState | 请求状态:0 1 2 3 4请求完毕 4 === xhr.DONE |
|
status | 响应状态码 | |
一些方法 | ||
open | 初始化一个请求 | |
setHeaders | myReq.setRequestHeader(header, value); |
设置头部信息必须在open和send之间 |
send | 发送请求 | |
一些事件 | ||
onerror | 错误发生的回调 | |
timeout | 超时回调 | |
onload |
在来看一个完整例子
let httpRequest;
if (window.XMLHttpRequest) {
httpRequest = new XMLHttpRequest()
} else {
console.log('出错了,请升级您的浏览器,目前版本不支持发送HTTP请求!')
}
function makeRequest() {
// 1 请求逻辑
httpRequest.onreadystatechange = resHandler
httpRequest.open('GET', 'http://www.baidu.com', true)
// 请求方法必须大写,第三参数标识是否异步
httpRequest.send()
}
function resHandler() {
// 2 响应数据处理逻辑
console.log('[]:', httpRequest.readyState)
if (httpRequest.readyState === XMLHttpRequest.DONE) { // 或者===4
if (httpRequest.status === 200) {
console.log('收到服务器响应数据')
console.log('[返回为字符串]:', httpRequest.responseText)
} else {
console.log('出错!', httpRequest.status)
}
}
}
makeRequest()
// 启动
ajax用于无刷新请求数据,它的一个经典案例是
文件上传
1.2 文件上传
提交表单和上传文件共有两种方法:
- 使用ajax,灵活,但复杂
- FormData API,简单,但无法使用JSON.stringify转化为一个对象字符串(why?),
因为xhr.send(new FormData(elForm))无法拿到数据
1)使用ajax提交数据
有两个原因,这种方法摒弃:1代码较为复杂 2兼容性差
但我在阅读的时候,有一个疑惑,在MDN上说:
在使用ajax上传文件的时候,发送二进制内容的最佳途径是通过
ArrayBuffers
或Blobs
结合send()
方法甚至FileReader
API 的readAsArrayBuffer()
方法。但是,自从该脚本的目的变成处理 可字符串化 的原始数据以来,我们使用sendAsBinary()
方法结合FileReader
API 的readAsBinaryString()
方法。同样地,上述脚本仅当你处理小文件时行之有效。如果不打算上传二进制内容,就考虑使用FormData
API 来替代。
最后一句,如果处理不打算上传二进制内容就用FormData,就用FormData API代替
,
这里的二进制内容指的是什么?图片?可执行文件?
这里指的是二进制对象,能够被JSON.parse反序列化的字符串对象
2)使用FormData API提交数据【重点】
这也是目前通用方法
直接上案例,简单
<script>
function AJAXSubmit (oFormElement) {
if (!oFormElement.action) { return; }
var oReq = new XMLHttpRequest();
oReq.onload = ajaxSuccess();
if (oFormElement.method.toLowerCase() === "post") {
oReq.open("post", oFormElement.action);
oReq.send(new FormData(oFormElement));
# 核心代码
# 下面是兼容GET请求,作为扩展内容【了解】
} else {
var oField, sFieldType, nFile, sSearch = "";
for (var nItem = 0; nItem < oFormElement.elements.length; nItem++) {
oField = oFormElement.elements[nItem];
if (!oField.hasAttribute("name")) { continue; }
sFieldType = oField.nodeName.toUpperCase() === "INPUT" ?
oField.getAttribute("type").toUpperCase() : "TEXT";
if (sFieldType === "FILE") {
for (nFile = 0; nFile < oField.files.length;
sSearch += "&" + escape(oField.name) + "=" + escape(oField.files[nFile++].name));
} else if ((sFieldType !== "RADIO" && sFieldType !== "CHECKBOX") || oField.checked) {
sSearch += "&" + escape(oField.name) + "=" + escape(oField.value);
}
}
oReq.open("get", oFormElement.action.replace(/(?:?.*)?$/, sSearch.replace(/^&/, "?")), true);
oReq.send(null);
}
}
</script>
<form action="register.php" method="post" enctype="multipart/form-data"
onsubmit="AJAXSubmit(this); return false;">
<fieldset>
<legend>Upload example</legend>
<p>
姓名: <input type="text" name="firstname" /><br />
</p>
<p>
您的照片:
<input type="file" multiple name="photos[]">
</p>
<p>
<input type="submit" value="Submit" />
</p>
</fieldset>
</form>
我们来看看FormData是什么?
1.3 FormData
1)首先,我们看3行代码【注意】
// formData 只接受文件、Blob 或字符串,不能直接传递数组,所以必须循环嵌入
for (let i = 0; i < photos.files.length; i++) {
formData.append('photo', photos.files[i]);
}
会发现FormData不是一个简单的字典(map),因为字典中重名会被覆盖,FormData是一个对象,猜测内部维护了一个数组。以后可能会新增传入数组的方法。
FormData特别简单,他可以接受的值有文件、Blob 和字符串
var formData = new FormData();
formData.append(key, value, [name]);
[name] 第三个为可选参数,设置发送请求的头 Content-Disposition 指定文件名
2)特殊用例
var formData = new FormData(someFormElement);
直接将一个Form标签解析,但注意只能解析具有name属性的字段
formdata可以输入File,Blob,那么这两个又是什么?
1.4 File和Blob
Blob
对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成ReadableStream
来用于数据操作。
File
接口基于Blob
,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。通常情况下,File对象的来源为input标签输入的FileList
对象数组,File没有啥特殊功能,暂不关注。
首先,我们来看一下Blob如何创建
var aBlob = new Blob( array, options );
- array 是一个由
ArrayBuffer
,ArrayBufferView
,Blob
,DOMString
等对象构成的Array
- options一般有两个属性,type和endings
创建一个Blob对象试试:
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; // 一个包含DOMString的数组
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // 得到 blob
刚刚说Blob中可以放ArrayBuffer,来看看它是什么
1.4.1 ArrayBuffer
ArrayBuffer
对象用来表示通用的、固定长度的原始二进制数据缓冲区。它是一个字节数组,通常在其他语言中称为“byte array”。
最基本的
const buffer = new ArrayBuffer(8);
// 创建8个字节的缓冲区
var view = new Int32Array(buffer);
// 用一个有符号32位类型化数组来用这个缓冲区
典型案例,从文件中获取buffer
function file2buffer(file){
return new Promise((re,rj)=>{
const rd = new FileReader()
rd.onload = e => re(e.target.result)
rd.onerror = e => rj(rd.error)
// 先监听,后读取
rd.readAsArrayBuffer(file)
})
}
上面相当于File转buffer,那么buffer转File呢?
首先,来看看File构造函数
var myFile = new File(bits, name[, options]);
- bits一个包含ArrayBuffer,ArrayBufferView,Blob,或者 DOMString 对象的 Array — 或者任何这些对象的组合。这是 UTF-8 编码的文件内容。
- name
USVString
,表示文件名称,或者文件路径。
// 那么转File就很简单
new File([buf], filename);
再看,Blob与Buffer的转换,其实Blob与File类似,所以把所有的File变成Blob就可以了。
new Blob([buf], filename);