Ajax:XMLHttpRequest
- XMLHttpRequest
- get
- url
- post
- 数据交换格式
- XML
- json
- XMLHttpRequest Level 2
- 请求时限
- 表单数据操纵
- 文件上传
XMLHttpRequest
XMLHttpRequest
简称 xhr
,是浏览器提供的 Javascript
对象,通过它可以请求服务器上的数据资源。 jQuery
中的 Ajax
函数,就是基于 xhr
对象封装出来的。
get
使用xhr
发送get
请求方法如下:
- 创建
xhr
对象 - 调用
xhr.open()
方法 - 调用
xhr.send()
方法 - 监听
xhr.onreadystatechange
事件
示例:
<script>// 1. 创建 XHR 对象let xhr = new XMLHttpRequest()// 2. 调用 open 函数xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts')// 3. 调用 send 函数xhr.send()// 4. 监听 onreadystatechange 事件xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {// 获取服务器响应的数据console.log(xhr.responseText)}}
</script>
事件监听中,包含了三个属性:
xhr.readyState
:xhr
对象的请求状态xhr.status
:是服务器的响应状态xhr.responseText
:服务器响应的数据
其中readyState
取值如下:
值 | 状态 | 含义 |
---|---|---|
0 | UNSENT | xhr 对象已经创建,但是没有open |
1 | OPENED | xhr 已经调用open() |
2 | HEADERS_RECEIVED | xhr 已经调用send() ,并且接收到服务器的响应头 |
3 | LOADING | 已经接收到部分响应报文 |
4 | DONE | Ajax 请求完成,表示请求彻底完成或者失败 |
当DONE
时,有可能是请求失败,此时还要检测xhr.status
是否成功。
如果在发送get
请求时,需要带参数,在open
时直接以?key=value
的形式追加到url
的末尾:
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts?id=1')
url
这种以?
的形式,在地址末尾进行传参,称为查询字符串
。把要传递的参数以?key=value
的形式追加到末尾,如果有多个参数,参数之间用&
分隔:
url?key1=value1&key2=value2&key3=value3
不论是以jQuery
的Ajax
,还是xhr
,只要发送的是get
请求,都是以查询字符串的形式携带参数。
如果发送的请求中,参数包含中文:
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts?id=1&text=你好世界')
此时中文就会被进行编码:
可以看到,发送除去的请求,text=%....
一串带有多个%
的乱码,每个%
后面是一个两位的十六进制数字,每三个%XX
表示一个中文字符。
因为在url
中,只允许出现英文字母,标点符号,数字,不允许出现中文,所以中文会被进行特殊编码。
JavaScript
提供了方法encodeURI
和decodeURI
,可以直接进行编码和解码:
<script>let str = '你好世界'let str2 = encodeURI(str) // 编码console.log(str2)let str3 = decodeURI(str2) // 解码console.log(str3)
</script>
输出结果:
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
你好世界
第一行是编码结果,第二行是解码结果。
post
使用xhr
发送get
请求方法如下:
- 创建
xhr
对象 - 调用
xhr.open()
方法 - 设置
Content-Type
属性 - 调用
xhr.send()
方法 - 监听
xhr.onreadystatechange
事件
示例:
<script>// 1. 创建 xhr 对象var xhr = new XMLHttpRequest()// 2. 调用 open 函数xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts')// 3. 设置 Content-Type 属性xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')// 4. 调用 send 函数xhr.send('id=1&text=你好世界')// 5. 监听事件xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 201) {console.log(xhr.responseText)}}
</script>
此处有两个注意点:
- 需要通过
xhr.setRequestHeader
方法设置Content-Type
属性,属性值为application/x-www-form-urlencoded
- 传递的参数作为
send
的参数,多个键值对以&
隔开,不再需要?
数据交换格式
所谓数据交换格式,其实就是客户端与服务器之间,数据要以统一的格式进行组织,方便双方进行数据交互,常见的交换格式为XML
和json
。
XML
XML
是一种是一种标记语言,语法格式与HTML
非常像。
<note><to>李四</to><form>张三</form><heading>通知</heading><body>晚上开会</body>
</note>
以上就是一个XML
格式的信息,表示张三给李四发送了一条通知,内容是晚上开会。可以看出,这种格式的数据语义非常明确易懂。
但是XML
会有很多额外的空间浪费,比如说以上内容中,标签比数据还多,会显得很臃肿,所以其实json
会使用的更多。
json
json
全称JavaScript Object Notation
,即JavaScript
对象表示法,它是由JavaScript
推出的一种数据格式化的形式,自然JavaScript
本身就对其有很强的解释能力,提供了很多接口完成这样的工作。
json
用字符串来表示数组和对象,通过数组与对象的嵌套,其可以表示很多复杂的数据结构。
{}
:包含一个对象,以key: value
的形式,以,
分隔各个键值对[]
:包含一个数组,以,
分隔各个元素value
其中,key
必须是字符串形式,而value
可以指数字、字符串、布尔值、null、数组、对象六种类型。
示例:
{"name": "张三","age": 20,"gender": "男","address": null,"hobby": ["HTML", "JavaScript", "Java", null]
}
这个json
最外层是一个{}
,表示这是一个对象,对象有五个属性,所有属性的key
都是字符串。注意,在json
中,所有字符串必须以双引号表示,不能使用单引号。
属性值中20
是数字,null
表示是空,"张三" "男"
都是字符串,而最后一个属性值嵌套了一个数组。
在数组中,无需以key: value
的形式存储元素,只存储value
,并且可以是六种类型之一。
[666,"hello world",["HTML", "JavaScript", "Java"],{"name": "张三","age": 18,"hobby": ["西瓜", "桃子", "哈密瓜"]}
]
以上json
,最外层是一个[]
,表示这是一个数组,数组无需key
,所以四个元素都是单独出现的,前两个元素666
和"hello world"
分别是数字和字符串。第三个元素是数组内嵌套的一个数组,第四个元素是数组内嵌套的一个对象。
而在第四个元素内部,由于是对象,所以又要以key: value
的形式填写数据,"hobby"
则是数组嵌套对象再嵌套数组,可以一直嵌套下去。
JavaScript
提供了方法,可以直接把一个符合要求的字符串转为对象。
语法:
JSON.parse("str")
示例:
let jsonStr = `
{"name": "张三","age": 20,"gender": "男","address": null,"hobby": ["HTML", "JavaScript", "Java", null]
}
`
let obj = JSON.parse(jsonStr)
console.log(obj)
此处jsonStr
就是一个符合要求的字符串,通过JSON.parse(jsonStr)
将其转化为对象,并输出。
输出结果:
得到了一个属性值和字符串描述一模一样的对象。
当然,以上的json
是带有换行格式的字符串,就算把所有内容写在同一行,也可以正常解析:
let jsonStr = '{"name": "张三","age": 20,"gender": "男","address": null,"hobby": ["HTML", "JavaScript", "Java", null]}'
虽然这样json
的可读性会降低,但是因为减少了换行,其实传输效率会变高。
如果已有一个对象,也可以通过方法直接转化为json
字符串。
语法:
JSON.stringify(obj)
示例:
let obj = {name: "张三",age: 20,gender: "男",address: null,hobby: ["HTML", "JavaScript", "Java", null]
}let jsonStr = JSON.stringify(obj)
console.log(jsonStr)
输出结果:
这样就拿到了转化为json
字符串格式的对象了,而且默认是不带换行的格式。
XMLHttpRequest Level 2
xhr Level 2
是xhr
的新版本,其提供了更加丰富的功能:
- 允许用户设置请求的时间限制
- 可以使用
FormData
管理表单数据 - 可以上传文件
请求时限
有的时候网速比较慢,用户要等待很久才能完成一个请求,此时为了避免不必要等待,可以设置请求时限,超出一定时间没有完成请求,直接失败。
其用法很简单,只需要添加一个属性即可:
xhr.timeout = 超时时限
其中时限以毫秒为单位,如果超出时限还没有获取响应,此时直接返回,并触发一个回调函数:
xhr.ontimeout = function(event){}
这个回调函数,只需要设置到ontimeout
属性中即可。
表单数据操纵
为了方便表单数据的处理,新增了一个FormData
对象,其可以模拟表单的操作,快速完成Ajax
表单数据的发送。
流程:
- 创建
FormData
对象 - 为
FormData
对象添加表单项 - 创建
xhr
对象 - 在
xhr.send()
时,直接以FormData
对象作为参数
示例:
let fd = new FormData()
fd.append('name', "张三")
fd.append('age', 18)let xhr = new XMLHttpRequest()
xhr.open('POST', '###')
xhr.send(fd)
首先 new FormData()
得到一个新的对象,append
方法用于添加表单项。通过xhr
的open
方法发送POST
请求,随后send
内直接把fd
发送出去,而不需要考虑表单内容的字符串拼接等操作。
另外的,FormData
也可以直接读取表单中的数组,而不用一个个append
。
示例:
let form = document.querySelector('#form1')
form.addEventListener('submit', function (e) {e.preventDefault()let fd = new FormData(form)let xhr = new XMLHttpRequest()xhr.open('POST', '###')xhr.send(fd)
})
首先通过querySelector
获取到表单的DOM
对象,对表单的提交事件绑定函数。
随后在函数内部完成发送表单,先阻止默认事件,随后创建FormData
对象,直接在构造时new FormData(form)
,这样就把表单form
的内容直接初始化到了FormData
中。随后就可以通过Ajax
发送出去了。
文件上传
想要通过Ajax
发送文件,使用和表单相同的接口。
流程:
- 获取上传文件的标签的
DOM
对象 - 获取
DOM
对象的files
属性 - 直接把
files
添加到FormData
中 - 把
FormData
通过xhr
发送出去
示例:
<input type="file" id="file1" />
<button id="btnUpload">上传文件</button>
这是两个标签,input
标签用于上传文件,button
用于提交文件。
<script>let btnUpload = document.querySelector('#btnUpload')btnUpload.addEventListener('click', function () {let files = document.querySelector('#file1').filesif (files.length <= 0) {return alert('没有选择文件!')}let fd = new FormData()// 将用户选择的文件,添加到 FormData 中fd.append('file', files[0])let xhr = new XMLHttpRequest()xhr.open('POST', '###')xhr.send(fd)xhr.onreadystatechange = function () {// ...}})
</script>
为button
绑定点击事件,在时间内部完成文件上传。首先获取到input
文件上传标签document.querySelector('#file1')
,在这个对象的.files
属性中,包含了用户上传的文件。
这个.files
是一个数组,每个数组元素代表一个用户上传的文件。如果files.length <= 0
说明用户没有上传文件,直接返回。
随后新建一个FormData
对象,把files[0]
添加到表单对象中,也就是把用户上传的第一个文件提交上去,随后通过xhr
发送给服务器。要注意的是,上传文件必须通过POST
请求。
文件上传也可以通过jQuery
来代替xhr
完成:
$.ajax({method: 'POST',url: '###',data: fd,processData: false,contentType: false,success: function (res) {console.log(res)}
})
在jQuery
发送文件时,通过data
属性指定要上传的表单对象,如果表单内部的内容是文件,那么processData
和contentType
要设置为false
。
processData
:不修改Conten-Type
属性,使用FormData
默认值contentType
:不对数据进行编码,以原本的数据格式发送出去(防止文件格式错误)