Ajax(Asynchronous JavaScript And XML)是2005年新出现的技术,它的出现是为了解决这样一个场景:整个页面中,只有一小部分的数据需要进行更新,按照传统的前后端交互,我们需要向服务器请求该网页的所有数据,然后再在客户端重新渲染,这无疑是非常低效的操作。因此,Ajax就可以做到只向服务器请求我们想要的那一小部分数据,而不用请求全部数据,进而在刷新整个页面的前提下更新那部分的数据。
举个例子,我们去饭店吃饭,然后点了一桌子菜,后来发现其中有一道菜太咸了,因此我们只需要让服务员端回去给厨师重新做这一道菜再拿回来就行了。
吃饭事件 | 数据更新 |
---|---|
我们 | 客户端 |
菜品 | 页面所有的数据 |
服务员 | ajax对象 |
厨师 | 服务器 |
当我们发现有一道菜太咸了,不需要让厨师把所有的菜重新做一遍,只要让服务员拿这一道菜回去给厨师重做这一操作就相当于让ajax对象向后端请求那一小部分数据再拿回来更新页面而无需刷新整个页面。
当我们发现有一道菜太咸了,不需要让厨师把所有的菜重新做一遍,只要让服务员拿这一道菜回去给厨师重做这一操作就相当于让ajax对象向后端请求那一小部分数据再拿回来更新页面而无需刷新整个页面。
Ajax的基本流程:创建XHR对象 => 发送数据 => 接收数据
既然Ajax涉及到前后端的数据交互,那么我们就先来简单的看一下几种类型的状态码,如下表:
状态码 | 含义 |
---|---|
100 ~ 199 | 连接含义 |
200 ~ 299 | 各种成功的请求 |
300 ~ 399 | 重定向 |
400 ~ 499 | 客户端错误 |
500 ~ 599 | 服务端错误 |
在使用xhr之前,我们要创建一个xhr的实例对象:
let xhr = new XMLHttpRequest()
然后再调用xhr对象上的 open() 方法,表示创建一个请求。
open()
方法接收三个参数:
// 创建了一个Ajax请求
xhr.open('get', 'example.php', 'true')
光调用了 open()
方法还不够,它只是创建了一个请求,但还没有发送请求,因此我们还要调用xhr对象上的另一个方法,即 send()
方法,表示将请求发送给目标URL
send()
方法接收一个参数:
// 我们上面创建的是get请求,因此send()方法无需传参
xhr.send()
请求发送出去后,客户端需要接收服务器响应回来的数据,xhr对象中有一些属性,它们存储着服务端返回来的一些数据信息,如下表所示:
属性名 | 含义 |
---|---|
response Text | 服务端返回的文本信息 |
responseXML | 服务端返回的XML DOM 文档 |
status | HTTP 状态码 |
status Text | HTTP状态码说明 |
readyState | xhr对象的请求响应阶段 |
既然我们要获取服务端返回的数据,我们就要知道服务端是何时返回数据的,这就可以通过上面表格中的 readyState 属性来判断了
readyState
属性一共有5个值,分别表示不同的请求响应阶段:
同时,xhr对象可以绑定一个 readystatechange 事件,每当 readyState 属性发生改变,都会触发该事件,因此,该事件在一次请求中会被多次触发
xhr.onreadystatechange = function() {
console.log('readyState属性发生改变了')
}
所以,我们可以在 readystatechange
事件中判断一下 readyState
属性是否为 4,即是否已经接收所有的响应,然后还可以再继续判断一下 status
属性,看看状态码是否为 200,当上述都成立了,我们再去 responseText
属性 或 responseXML
属性中获取响应数据
xhr.onreadystatechange = function() {
// 判断是否已接收所有响应
if(xhr.readyState === 4) {
// 判断状态码是否为200
if(xhr.status === 200) {
console.log(xhr.responseText)
}
}
}
//封装一个ajax请求
function ajax(options) {
//创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()
//初始化参数的内容
options = options || {}
options.type = (options.type || 'GET').toUpperCase()
options.dataType = options.dataType || 'json'
// 处理参数
let str = ''
let params = options.data;
for (let key in params) {
str += key + '=' + params[key] + '&'
}
params = str.slice(0, str.length - 1)
//发送请求
if (options.type === 'GET') {
xhr.open('GET', options.url + '?' + params, true)
xhr.send(null)
} else if (options.type === 'POST') {
xhr.open('POST', options.url, true)
// post 请求需要设置请求头 模仿表单请求
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(params)
}
//接收请求
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
let status = xhr.status
if (status >= 200 && status < 300) {
options.success && options.success(xhr.responseText, xhr.responseXML)
} else {
options.fail && options.fail(status)
}
}
}
}
上面也讲解了Ajax请求的简单应用,同时也是拿 get 请求来举得例子,因此这里我就不多做说明,唯一要讲的就是,get请求所携带的数据是明文的,大小只有4k左右,而且它是写在URL的 ? 后面的,例如这样 example.php?query=4&em=0,所以若是我们要在发送get请求时携带数据,只需要在调用 open() 方法时,将数据写在第二个参数的URL的 ? 后面即可
let btn = document.querySelector('.btn')
btn.addEventListener('click', function () {
ajax({
type: 'get',
dataType: 'json',
data: { uid: '64dcd451a2d7172b77c03768', aid: "64db6361c57b44a4c47712af" },
url: 'http://localhost:3456/wyc/getUser',
success: function (text, xml) {//请求成功后的回调函数
console.log(JSON.parse(text))
},
fail: function (status) {请求失败后的回调函数
console.log(status)
}
})
})
发送post请求的过程几乎和get请求一样,唯一不一样的是数据的传递。大家都知道post请求的数据是放在请求体中的,因此我们需要调用xhr对象上的 setRequestHeader() 方法来模仿表单提交时的内容类型
该方法传入的参数比较固定,代码如下
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
然后我们上面也说过,send() 方法接收的一个参数是请求主体发送的数据,所以我们的post请求要发送的数据就要作为该方法的参数,代码如下:
xhr.send('query=4&em=0')
完整post请求:
let btn = document.querySelector('.btn')
btn.addEventListener('click', function () {
ajax({
type: 'post',
dataType: 'json',
data: { uid: '64dcd451a2d7172b77c03768', aid: "64db6361c57b44a4c47712af" },
url: 'http://localhost:3456/wyc/attention',
success: function (text, xml) {//请求成功后的回调函数
console.log(JSON.parse(text))
},
fail: function (status) {请求失败后的回调函数
console.log(status)
}
})
})
了解过同源策略以后,我们来看看如何让Ajax不受同源策略的限制而成功发送请求。CORS
(跨域资源共享)要求我们在发送请求时自定义一个HTTP头部与服务器进行沟通,我们只需要设置一个名为 Origin 的头部,值为当前页面的源信息(协议、域名、端口),例如 Origin : http://example.com
;然后服务器需要设置一个名为 Access-Control-Allow-Origin
的响应头部,其值为允许跨域访问的源信息,若服务器设置的 Access-Control-Allow-Origin
与我们设置的 Origin 相同,则表示服务器允许我们跨域请求其资源,或者服务器可以将 Access-Control-Allow-Origin
值设为 *,此时表示允许任何域向其发送请求并且不受同源策略的限制。