目录
- 摘要
- 1.post方法
- 2.create方法
- 3.拦截器
- 4.代码
摘要
在vue中,我们调用接口使用的都是axios,使用之前我们也会进行一定的封装,然后再进行使用。
在这里,我们主要说一下axios的实现原理,以及如何使用原生的js来自己封装出一个axios。
这里实现出几个主要的方法,包括post请求方法,create配置方法,以及拦截器的方法。
1.post方法
在我们写方法之前,肯定是要先自己写一个类出来,里面的内容先不用写。
然后再在类的下面写出post的方法:
function iaxios () {
}
iaxios.prototype.post = function (url, data){
}
OK,现在我们来实现这个方法。
如果我们想用原生的js来实现post请求,我们应该这样做:
var xhr = new XMLHttpRequest();
xhr.open('post', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status == 200 || xhr.status == 304)) {
resolve(xhr.responseText)
} else {
reject(xhr.responseText)
}
}
};
xhr.send(data)
但是我们直接把这段代码放进去的话,会有一点问题:
1.官方的axios返回的是一个promise对象
2.官方的axios.post方法传的data参数是一个对象,原生的是一个字符串
所以为了解决第一个问题,我们可以在方法内返回一个promise对象,而第二个问题可以使用JSON.stringfy()这个方法解决。
iaxios.prototype.post = function (url, data) {
//返回promise对象
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('post', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status == 200 || xhr.status == 304)) {
resolve(xhr.responseText)
} else {
reject(xhr.responseText)
}
}
};
xhr.send(JSON.stringify(data))
})
}
这样我们就实现了post的这个方法了。
而对get方法这里就不再写一遍了(写的时候主要注意参数的问题就可以了)。
2.create方法
首先我们知道,axios的create方法主要是对请求头等参数的配置,而这个方法同样也会返回一个axios的实例,这个实例可以继续使用post等方法。
所以我们首先想到的是,这个方法应该返回一个实例对象,而这个实例对象下的属性会根据传进来的对象设置。
这里面我只列举两个属性:
iaxios.prototype.create = function (obj) {
var emaxios = new iaxios()
emaxios.headers = obj.headers;
emaxios.baseUrl = obj.baseUrl;
return emaxios;
}
但是我们调用完这个create方法后,我们已经有了一些配置,如果再调用post等方法,我们应该是携带着这些参数去调用psot方法,所以在post方法内我们也要修改一下。
我们首先写一个配置请求头的方法:
function setHeader (xhr, headers) { for (var i in headers) { xhr.setRequestHeader(i, headers[i]); } }
然后在post方法内,在ajax请求发送之前调用这个方法就可以了:
setHeader(xhr, this.headers);
3.拦截器
在axios中,拦截器分两种,分别是请求拦截和响应拦截。
二者的作用也是相差不多的,一个是希望在请求之前做什么,一个是希望在请求之后做什么。
我们先来写一下请求拦截(这里面不考虑use):
我们先想一下,interceptors一定是一个对象,并且下面有两个属性,分别是request和response。
那么我们就在类里面写一下:
function iaxios () {
this.interceptors = {
request (cb) {
},
response (aa) {
},
}
}
如果我们想在外边调用这个方法,会将一个回调函数传进来,
所以在我们这里我们需要把这个回调函数保存下来(考虑到有多个拦截器,所以我们采用数组保存)。
同时我们也要写一个data用于存放请求时的数据(因为在请求拦截器的回调函数中,有一个config参数,它包含着请求的数据)
function iaxios () {
//保存拦截器中的回调函数
this.saveRequest = []
this.saveResponse = []
//保存请求的数据
this.data = {};
let _this = this;
this.interceptors = {
request (cb) {
_this.saveRequest.push(cb)
},
response (aa) {
_this.saveResponse.push(aa)
},
}
}
现在拦截器的方法已经写好了,我们应该在什么时候调用呢?
请求拦截一定是在请求发送之前进行调用,我们拿post来举例子:
iaxios.prototype.post = function (url, data) {
this.data = data;
let _this = this;
// this.saveRequest && this.saveRequest(this)
//请求之前调用请求拦截的回调函数
if (this.saveRequest) {
this.saveRequest.forEach(fn => {
fn(this)
})
}
我们只需要在请求之前,把想要传递的数据copy下来(用于当作参数)。
然后循环调用我们保存下来的方法(其实就是调用的时候传递回调函数)
这样就能实现请求拦截了。
那么响应拦截一定是在返回响应数据之前调用了:
if ((xhr.status == 200 || xhr.status == 304)) {
//用来保存返回的数据
let newRespose = new Object;
newRespose.data = JSON.parse(xhr.responseText);
//在返回数据之前调用相应拦截器的回调函数
if (_this.saveResponse) {
_this.saveResponse.forEach(fn => {
fn(newRespose)
})
}
resolve(newRespose.data)
这里面我们创建的newRespose也是为了当作参数传递到响应拦截的方法里面。
这样的话响应拦截器的实现也完成了。
4.代码
最后再把整体的代码复制一份:
// const { resolve, reject } = require("core-js/fn/promise")
function iaxios () {
//保存拦截器中的回调函数
this.saveRequest = []
this.saveResponse = []
//保存请求的数据
this.data = {};
let _this = this;
this.interceptors = {
request (cb) {
_this.saveRequest.push(cb)
},
response (aa) {
_this.saveResponse.push(aa)
},
}
}
iaxios.prototype.post = function (url, data) {
this.data = data;
let _this = this;
// this.saveRequest && this.saveRequest(this)
//请求之前调用请求拦截的回调函数
if (this.saveRequest) {
this.saveRequest.forEach(fn => {
fn(this)
})
}
//返回promise对象
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('post', url, true);
//设置请求头的配置
setHeader(xhr, this.headers);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status == 200 || xhr.status == 304)) {
//用来保存返回的数据
let newRespose = new Object;
newRespose.data = JSON.parse(xhr.responseText);
// _this.saveResponse && _this.saveResponse(newRespose)
//在返回数据之前调用相应拦截器的回调函数
if (_this.saveResponse) {
_this.saveResponse.forEach(fn => {
fn(newRespose)
})
}
resolve(newRespose.data)
} else {
reject(xhr.responseText)
}
}
};
xhr.send(JSON.stringify(data))
})
}
iaxios.prototype.get = function (url) {
let _this = this;
// this.saveRequest && this.saveRequest(this)
//请求之前调用请求拦截的回调函数
if (this.saveRequest) {
this.saveRequest.forEach(fn => {
fn(this)
})
}
//返回promise对象
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
//设置请求头的配置
setHeader(xhr, this.headers);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status == 200 || xhr.status == 304)) {
//用来保存返回的数据
let newRespose = new Object;
newRespose.data = JSON.parse(xhr.responseText);
// _this.saveResponse && _this.saveResponse(newRespose)
//在返回数据之前调用相应拦截器的回调函数
if (_this.saveResponse) {
_this.saveResponse.forEach(fn => {
fn(newRespose)
})
}
resolve(newRespose.data)
} else {
reject(xhr.responseText)
}
}
};
xhr.send()
})
}
//返回一个新的实例并且复制obj的属性
iaxios.prototype.create = function (obj) {
var emaxios = new iaxios()
emaxios.headers = obj.headers;
emaxios.baseUrl = obj.baseUrl;
return emaxios;
}
//设置请求头的方法
function setHeader (xhr, headers) {
for (var i in headers) {
xhr.setRequestHeader(i, headers[i]);
}
}
var taxios = new iaxios();
export {
taxios
}