Ajax 全称 Asynchronous JavaScript And XML 及异步JS与XML,其主要原理是使用XMLhttpRequest对象完成前后端交互,
它通过状态码readyState区别当前状态,分别为
0.未定义
1.载入中
2.载入完成
3.交互中
4.交互完成
可通过状态码改变执行操作,而平时使用的ajax都是封装好的一个程序,他的封装过程如下:
1.获取请求对象,内部应当包含请求类型,接口url和上传服务器的数据
2.新建httpRequest对象
let xhr = new XMLHttpRequest()
3.将请求配置参数赋值给httpRequest对象
xhr.open('get', obj.url + toValue(obj.data), obj.isAsync)
4.发送
xhr.send()
5.状态更改回调检测到数据返回并执行相应操作
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
解析后端返回数据
}
}
一个简单的ajax封装应当是这样的
//封装一个ajax 即XMLHttpRequest
/*
使用说明
以一下格式发送数据
ajax({
url: '/login',
method: 'get',
data: {
username:'xh1001',
password:'T01'
},
isAsync: 'true',
success:function(res){
console.log("res:",res.data);
},
error:function(err){
console.log("err:",err);
}
})
*/
/*分割线************************ */
function ajax(obj) {
//判断接口是否为空
if (!obj.url) {
return;
}
//1.数据检查及补充
//请求种类,默认为get
obj.method = obj.method || 'get'
//是否异步,默认为true
obj.isAsync = obj.isAsync || 'ture'
//是否有数据,没有默认补充为空对象
obj.data = obj.data || {}
// 2.准备ajax
let xhr = new XMLHttpRequest() || new ActiveXObject()//ie兼容
// 发送get或者post
if (obj.method === 'get') {
// 准备发送数据
xhr.open('get', obj.url + toValue(obj.data), obj.isAsync)
// 发送get
xhr.send()
} else if (obj.method === 'post') {
// 准备发送数据
xhr.open('post', obj.url, obj.isAsync)
// 配置请求头content-type 申明数据以什么方式解析数据(json)
xhr.setRequestHeader('Content-type', 'application/json')
// 配置请求头content-type 申明数据以什么方式解析数据(表单数据)
// xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
// 发送post(json)
xhr.send(JSON.stringify(obj.data))
// 发送post(表单数据)
// xhr.send(toValue(obj.data))
}
// 检测readyState判断是否将数据传回
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
//返回数据已obj的success回调函数输出
// console.log('====',xhr.responseText);
// obj.success(JSON.parse(xhr.responseText))
// obj.success(xhr.responseText)
// 检测cookie,如果cookie无效将执行
if (JSON.parse(xhr.responseText).code === 201) {
console.log(JSON.parse(xhr.responseText));
.....
} else {
obj.success(JSON.parse(xhr.responseText))
}
} else {
obj.error('unknowError')
}
}
}
}
//前端数据拼接为表单格式
const toValue = function (obj) {
let str = '?'
for (let key in obj) {
str += `${key}=${obj[key]}&`
}
return str.slice(0, str.length - 1)
}
其中 // xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')这里的setRequestHeader其实是对请求头进行配置,而Content-type具体是对请求的数据解码方式进行说明,以便后端服务器处理数据,
常用的有application/x-www-form-urlencoded(原生form表单)、application/json(json数据)、multipart/form-data(带文件上传功能得数据盒子,一个对象)
这样在页面中只要调取ajax({datas})程序 ,就可以发送ajax请求了
当然数据的处理不是一概而论的,所以我们在发送ajax时输入的对象中配置成功或失败的回调(这里只针对返回的数据而言成功与否,并不是对ajax请求)
ajax({
url: '/studentCodeChange',
method: 'post',
data: {
id: nowUser.s_id,
oldCode: oldCode,
newCode: newCode
},
success: function (data) {
console.log("data", data);
// 清空输入框
alert('修改密码成功')
$('#codeChangeLab').css('display', 'none')
$('#oldCode').val('')
$('#newCode').val('')
$('#reCode').val('')
},
error: function (err) {
console.log('err', err);
alert('查询失败,请稍后尝试')
}
})
axios本身是对ajax而言的一个二次封装,相比原生Ajax,它用到了promise技术,从而改变了ajax需要多次调用时的尴尬,在成功之后我们可以用.then().catch()轻易进行链式调用,使得代码可读性更高,同时promise可以更方便地对请求本身进行修改,从而很好地解决了身份验证,地址传值等处的代码量,我们在使用时可以简单地调用它,也可以进行复杂的逻辑操作。
一个简答的axios请求是这样的:
const axios= axios.create({
baseURL: '/api', // 给axios请求的 url 前面加一个 '/api'
timeout: 5000, //整体设置axios的请求超时时间
// headers: {'X-Custom-Header': 'foobar'} //整个加请求头
})
axios({
url: '/SystemSetting/login',
method:'post',
data: data
}).then((res)=>{
do sth......
})
axios({
url: '/SystemSetting/login',
method:'get',
params: data
}).then((res)=>{
do sth......
})
正如上面所说的 ,我们可以对axios进行一定的封装,通常封装的目的是
1.配置baseUrl
通常项目开发时,需要跨域,在我们设置了跨域服务器后,请求也需要向对方服务器发送,所以要配置一个变量作为请求头,而这里的baseUrl激素hi为了统一配置方便开发,开发完成后置空或者删除再部署到服务器
2.拦截器配置
axios.interceptors.request.use
3.设置延迟时间
请求不能一直等待,当到达一定时限后要取消发送并返回请求超时
import axios from 'axios'
const http = axios.create({
baseURL: '/api', // 给axios请求的 url 前面加一个 '/api'
timeout: 5000, //整体设置axios的请求超时时间
// headers: {'X-Custom-Header': 'foobar'} //整个加请求头
});
// 以下是拦截器
//拦截请求 use 接收两个参数 第一个是请求成功的回调函数,第二个请求失败的回调函数
http.interceptors.request.use(function (req) {
//再整整发起请求前的操作
// console.log('req',req);
//如果有token的话,就携带上,没有不携带
if(localStorage.getItem('token')){
//携带在请求里面
req.headers['token'] = localStorage.getItem('token')
}
return req; //放行
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
//拦截响应
http.interceptors.response.use(function (res) {
//响应回来 做的操作
// console.log('相应回来的数据',res);
//存token
if(res.data.data && res.data.data.token){
localStorage.setItem('token',res.data.data.token)
}
//简写
// res.data.token && localStorage.setItem('token',res.data.token)
//判断res里面有没有token,有的话就存起来,没有就不管
return res; //放行
},
function (error) {
console.log('err111',err);
//判断一下响应的状态码
if(error.response.status === 401){
alert('身份过期 没有携带token')
//把token清除掉
localStorage.removeItem('token')
//跳转到等录取 重新验证身份
}
return Promise.reject(error);
}
);
export default http
fetch是基于promise的一个原生语法糖,关于他的用法,官方给出了一个很完善的例子
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.请求方式
mode: 'cors', // no-cors, *cors, same-origin 是否开启代理
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached缓存
credentials: 'same-origin', // include, *same-origin, omit是否同源
headers: {
'Content-Type': 'application/json'数据类型
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error重定向
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match "Content-Type" header同请求头
//格式
});
return response.json(); // parses JSON response into native JavaScript objects
}
postData('https://example.com/answer', { answer: 42 })
.then(data => {
console.log(data); // JSON data parsed by `data.json()` call
});
一般使用的时候还是要封装,我们再举一个简单的封装案例:
function http({url,method,data={},headers={}}){
console.log(method)
console.log(data)
if(!url){
return
}
// 常用请求头
headers["Content-type"] = "application/json"
headers["token"] = "token@@@@@"
if(method.toLowerCase()==="get"){
data = JSONtoUrl(data)
console.log("getting")
let newUrl = url+'?'+data
let init = {method:"get"}
return fetch(newUrl,init).then((res)=>{res.json()})
}else{
console.log("posting")
let init = {
method:"post",
body:JSON.stringify(data),
headers
}
console.log(init)
console.log(init.body)
return fetch(url,init).then((res)=>{res.json()})
}
}
function JSONtoUrl(json){
let str = ''
for(let k in json){
str += `${k}=${json[k]}&`
}
str = str.substring(0,str.length-1)
return str
}
export default http
这里要注意这一步不能少
.json的作用是将promise对象解析为一个接送对象,官方的说法是fetch返回的是一个完整的http请求的相应格式,并不能直接被我们使用,这里就需要他们设定的json()方法进行解析,由于基于promise,我们直接将其封装在返回值内,这样就可以直接得到后端原本发给我们的数据了。
return fetch(newUrl,init).then((res)=>{res.json()})
封装后发送
let packageGet = ()=>{
http({
url:'x',
method:'get',
data:{name:"sss",age:18},
}).then((res)=>{
console.log(res)
})
}
let packagePost = ()=>{
http({
url:'x',
method:'post',
data:{"name":"xxx","age":"18"},
}).then((res)=>{
console.log(res)
})
}
一个fetch