axios全面解析
axios解析
·
1. axios一些优点介绍
axios的特点以及使用_Luckyzhoufangbing的博客-CSDN博客_axios的优点
2. axios的使用
使用 npm:
$ npm install axios |
使用 bower:
$ bower install axios |
使用 cdn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<style>
.btn {
width: 200px;
height: 100px;
border: 1px salmon solid;
background-color: cadetblue;
color: aliceblue;
cursor: pointer;
}
</style>
</head>
<body>
<button class="btn">get</button>
<button class="btn">post</button>
<button class="btn">put</button>
<button class="btn">delete</button>
<script>
axios.defaults.baseURL = "http://127.0.0.1:7000"
axios.defaults.timeout = 3000
const btns = document.getElementsByClassName("btn")
btns[0].onclick = function () {
axios({
method: "get",
url: `get`,
params: {
name: "get"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.warn(err);
})
}
btns[1].onclick = function () {
// axios({
// method: "post",
// url: `http://127.0.0.1:7000/post`,
// data: {
// name: "post请求"
// }
// }).then(res => {
// console.log(res);
// }).catch(err => {
// console.warn(err);
// })
// 1,url 2, data 3, config
axios.post(
"/post",
{
name: "post请求"
})
}
btns[2].onclick = function () {
axios({
method: "put",
url: `/put`,
data: {
name: "put请求"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.warn(err);
})
}
btns[3].onclick = function () {
axios({
method: "delete",
url: `/delete`,
data: {
name: "delete请求"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.warn(err);
})
}
//也可以直接创建axios的对象
const request = axios.create({
//配置项
})
console.log(request);
</script>
</body>
</html>
3. 拦截器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<style>
.btn {
width: 200px;
height: 100px;
border: 1px salmon solid;
background-color: cadetblue;
color: aliceblue;
cursor: pointer;
}
</style>
</head>
<body>
<button class="btn">get</button>
<button class="btn">post</button>
<button class="btn">put</button>
<button class="btn">delete</button>
<script>
axios.defaults.baseURL = "http://127.0.0.1:7000"
axios.defaults.timeout = 3000
function bind() {
const btns = document.getElementsByClassName("btn")
btns[0].onclick = function () {
axios({
method: "get",
url: `get`,
params: {
name: "get"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.warn(err);
})
}
btns[1].onclick = function () {
axios.post(
"/post",
{
name: "post请求"
})
}
btns[2].onclick = function () {
axios({
method: "put",
url: `/put`,
data: {
name: "put请求"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.warn(err);
})
}
btns[3].onclick = function () {
axios({
method: "delete",
url: `/delete`,
data: {
name: "delete请求"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.warn(err);
})
}
}
bind()
//请求拦截器
axios.interceptors.request.use(function (config) {
//config就是该请求的配置
//例如 config.params={a:500}
console.log("请求拦截成功");
return config
}, function (err) {
console.log("请求拦截失败");
return Promise.reject(err)
})
//响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截成功");
//默认的响应结果
return response
}, function (err) {
console.log("响应拦截失败");
return Promise.reject(err)
})
</script>
</body>
</html>
4. 取消请求
let cancel = null
const btns = document.getElementsByClassName("btn")
btns[0].onclick = function () {
//发新的取消旧的
if(cancel){
cancel()
}
axios({
method: "get",
url: `get`,
params: {
name: "get"
},
cancelToken: new axios.CancelToken(function (c) {
//cancel现在就是取消事件了
cancel = c
})
}).then(res => {
console.log(res);
cancel = null
}).catch(err => {
console.warn(err);
})
}
5. 源码解析
(1).模拟制造过程
axios实例对象之所以又可以当函数又可以当对象调用上面的属性方法(axios(),axios.get())是因为源码中instance函数不仅本身是函数,并且使用extend函数将Axios显示原型上的方法全部继承了过来。instance不过就是Axios.prototype.request.bind().其实和request的功能一模一样。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Axios(config) {
this.defaults = config
this.interceptors = {
request: {},
response: {}
}
}
Axios.prototype.request = function (config) {
console.log("发送请求" + config.method);
}
Axios.prototype.get = function (config) {
return this.request({ method: "get" })
}
Axios.prototype.post = function (config) {
return this.request({ method: "post" })
}
function createInstance(config) {
//还不能当函数用
let context = new Axios(config)
//instance不能当对象用
let instance = Axios.prototype.request.bind(context)
//获取对象的方法
Object.keys(Axios.prototype).forEach((key) => {
instance[key] = Axios.prototype[key].bind(context)
})
//为他添加构造器属性
Object.keys(context).forEach(key => {
instance[key] = context[key]
})
return instance
}
let axios = createInstance({ method: "get" })
axios.get()
axios({method:"get"})
</script>
</body>
</html>
(2). 发送请求
request调用dispatch,dispatch调用我们的http或者xhr(因为要适配node环境和浏览器环境)。保证返回的响应,返回到我们axios函数的promise回调。
代码实现如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Axios(config) {
this.config = config
}
Axios.prototype.request = function (config) {
//发送请求
let promise = Promise.resolve(config)
let chains = [dispatchRequest, undefined]
let result = promise.then(chains[0], chains[1])
return result
}
function dispatchRequest(config) {
return xhrAdapter(config)
.then(res => {
//转化响应结果
return res
})
.catch(err => {
throw Error("err")
})
}
function xhrAdapter(config) {
return new Promise((resolve, reject) => {
//发送请求
let xhr = new XMLHttpRequest()
xhr.open(config.method.toUpperCase(), config.url)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve({
config,
data: xhr.response,
headers: xhr.getAllResponseHeaders(),
request: xhr,
status: xhr.status,
statusText: xhr.statusText
})
} else {
reject(new Error(xhr.status))
}
}
}
})
}
let axios = Axios.prototype.request.bind(null)
axios({
url: "http://127.0.0.1:7000/get",
method: "get"
})
</script>
</body>
</html>
(3) 拦截器
首先是原理,在发送请求时有一个chains,他其实就是我们指向任务的一个链,当添加请求拦截器时,会通过unshift逆序添加到chains的头部,当添加响应拦截器时会顺序push到我们的chains尾部。循环遍历chains挨着执行。
代码模拟
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Axios(config) {
this.config = config
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
Axios.prototype.request = function (config) {
let promise = Promise.resolve(config)
let chains = [dispatchRequest, undefined]
//处理请求拦截器
this.interceptors.request.handlers.forEach(item => {
chains.unshift(item.fulfilled, item.reject)
})
//处理响应拦截器
this.interceptors.response.handlers.forEach(item => {
chains.push(item.fulfilled, item.reject)
})
console.log(chains);
while (chains.length > 0) {
promise = promise.then(chains.shift(), chains.shift())
}
return promise
}
function dispatchRequest(config) {
return new Promise((reslove, reject) => {
reslove({
status: 200,
statusText: "ok"
})
})
}
function InterceptorManager() {
this.handlers = []
}
InterceptorManager.prototype.use = function (fulfilled, reject) {
this.handlers.push({
fulfilled,
reject
})
}
let context = new Axios({})
let axios = Axios.prototype.request.bind(context)
Object.keys(context).forEach(key => {
axios[key] = context[key]
})
//请求拦截器
axios.interceptors.request.use(function (config) {
//config就是该请求的配置
//例如 config.params={a:500}
console.log("请求拦截成功");
return config
}, function (err) {
console.log("请求拦截失败");
return Promise.reject(err)
})
//响应拦截器
axios.interceptors.response.use(function (response) {
console.log("响应拦截成功");
//默认的响应结果
return response
}, function (err) {
console.log("响应拦截失败");
return Promise.reject(err)
})
axios().then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
</script>
</body>
</html>
(4) 取消请求
这里可以说是把promise玩到了极致,这里有一种很有意思的开发思路,如果我们在运行过程中不确定是否会执行一段逻辑,可以将它设置为promise成功的回调,但是我们的这个promise的reslove可以赋值给外部变量,这样当外部变量函数执行时,promise状态改变,执行then中的成功回调,进而执行我们提前准备好的逻辑,如果这个外部函数永远不执行,那么该promise的状态就会一直保持是padding状态,该逻辑就永远不会执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>发送</button>
<button>取消</button>
<script>
function Axios(config) {
this.config = config
}
function CancelToken(executor) {
let reslovePromise
this.promise = new Promise((reslove) => {
reslovePromise = reslove
})
executor(function () {
reslovePromise()
})
}
Axios.prototype.request = function (config) {
return dispatchRequest(config)
}
function dispatchRequest(config) {
return xhrAdapter(config)
}
function xhrAdapter(config) {
return new Promise((reslove, reject) => {
let xhr = new XMLHttpRequest()
xhr.open(config.method, config.url)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr, status < 300) {
reslove(xhr.response)
} else {
reject(xhr.statusText)
}
}
}
if (config.cancelToken) {
config.cancelToken.promise.then(res => {
xhr.abort()
})
}
})
}
let context = new Axios({})
let axios = Axios.prototype.request.bind(context)
let btns = document.getElementsByTagName("button")
let cancel = undefined
btns[0].onclick = function () {
axios({
url: "http://127.0.0.1:7000/get",
method: "GET",
cancelToken: new CancelToken(function (c) {
cancel = c
}),
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
}
btns[1].onclick = function () {
cancel()
}
</script>
</body>
</html>
更多推荐
已为社区贡献2条内容
所有评论(0)