axios核心源码逻辑实现(二)
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
大家好,欢迎来到今天的axios的源码学习。在上一个篇章中我们完成了给自定义的axios添加属性方法等功能,并最后成功完成了预期的结果。废话不多说,直接来看我们今天要完成的目标。
目标
今天我们的目标是利用自己创建的axios来发送请求获取数据
执行结果如下
实现步骤
-
创建一个JSON文件,并开启服务器
-
创建Axios构造函数
-
在Axios原型上挂载request方法,在内部调用dispatchRequest函数等
-
声明dispatchRequest函数在内部完成相关业务
-
创建xhrAdapter函数
-
调用axios发送请求获取结果
具体功能实现
创建一个JSON文件,并开启服务器
创建一个db.json的文件,并在内部写入如下json格式的代码,内部的文本部分可根据自己喜好修改
{
"posts": [
{
"id": 1,
"title": "印象中的爱情",
"author": "顶不住那时间"
},
{
"id": 2,
"title": "婧月思",
"author": "贝贝"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
},
{
"body": "所以你倾斜",
"postId": 2,
"id": 2
}
],
"profile": {
"name": "typicode"
}
}
接着右键这个json文件打开终端,输入以下命令并运行,这样一来我们就开辟了一个本地服务器
回车
接着我们在新建的html文件中引入axios的官方文件,这样我们就调用一下axios来看看是否能访问本地的服务器
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
右键打开页面查看调试器打印的内容
结果显示打印成功,这就说明我们成功的开启了我们的本地服务器,并可使用。
创建Axios构造函数
在进行接下来的操作之前,我们先把刚刚引入的axios.js文件和下面调用的axiso函数删除,在这一步中我们将要创建自己的Axios构造函数。
创建Ajax构造函数的步骤和之前一样,这里不多赘述,如图
// 创建Axios构造函数
function Axios(config) {
this.default = config,
this.interceptors = {
request: '',
response: ''
}
}
挂载request方法
接着,我们要在Ajax构造函数的原型身上挂载request方法,供后面的ajax函数使用
// 挂载request函数并书写相关逻辑业务
Axios.prototype.request = function (config) {
}
在request内部,我们要先创建一个成功的promise,为后期的逻辑代码执行提供保障
可以看到,这里调用了原生Promise函数,并使用了resolve( )方法,并声明变量promise来接收,那此时变量promise一定是一个成功的promise对象。
接着我们要来创建一个chains数组,里面放入dispatchRequest函数和undefined数据类型,至于为什么要这样做,等本文结束后各位便可理解。
可以看到我们目前没有声明dispatchRequest函数,我们稍后会在外面申明一个dispatchRequest函数
接下来我们要调用promise.then( )方法执行chains数组里的变量,并最终把result返回,如下
// 挂载request函数并书写相关逻辑业务
Axios.prototype.request = function (config) {
// 创建一个成功的promise
let promise = Promise.resolve(config)
// 创建chains数组
let chains = [dispatchRequest, undefined]
let result = promise.then(chains[0], chains[1])
return result
}
这里理解一下,由于变量promise函数是一个成功的函数,所以then会执行chains[ 0 ], 也就是dispatchRequest函数,而最终返回的result必须是一个promise对象,等价于dispatchRequest函数执行的返回的结果也必须是一个promise对象
接着我们来创建dispatchRequest函数
这个函数接收参数config,在函数体内需要返回一个函数xhrAdapter,如下
// 创建dispatchRequest函数
function dispatchRequest(config) {
return xhrAdapter(config).then(res => {
return res
}, err => {
return err
})
}
可以看到xhrAdapter这个函数调用了then方法,那也就是说xhrAdapter函数的执行结果也必须是一个promise函数,那接着我们再次在外面声明一个xhrAdapter函数,函数体内返回一个promise对象
在这个实例对象promise的内部,我们要调用原生AJAX经常会用到的XMLHttpRequest( )方法,并发送请求,如图
这里就可以看出axios发送请求的核心原理还是利用了XMLHttpRequest()上的方法
接着就来到了我们很熟悉的操作,判断xhr的状态等等,调用onreadystatechange,如下
xhrAdapter函数体内完整代码:
function xhrAdapter(config) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open(config.method, config.url)
xhr.send()
xhr.onreadystatechange = function (config) {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve({
//配置对象
config: config,
//响应体
data: xhr.response,
//响应头
headers: xhr.getAllResponseHeaders(), //字符串 parseHeaders
// xhr 请求对象
request: xhr,
//响应状态码
status: xhr.status,
//响应状态字符串
statusText: xhr.statusText
});
} else {
//失败的状态
reject(new Error('请求失败 失败的状态码为' + xhr.status));
}
}
}
})
}
这里看到我们最终调用了resolve方法,在resolve函数中我们传入了一个对象,对象内部的属性名要和官方axios调用返回的结果值属性名一致,而属性值从xhr身上获取得到
这样一来,xhrAdapter函数的执行结果就是一个成功的promise对象,那再回到调用它的dispatchRequest函数,也是一个成功的promise函数,再往上回到request内部,调用的chains[ 0 ]就是一个成功的promise函数,所以then的返回值也是一个成功的promise函数,那最终的result也是一个成功的promise函数
调用axios发送请求获取结果
最后,我们利用Axios原型上的request函数,利用bind方法返回一个新的函数axios。
这里可以理解为axios只是一个躯壳,其内部实质还是request函数
接着我们调用axios函数,填入请求路径和请求方法,并用变量p来接收axios的执行结果,打印p
打印结果
可以看到p是一个成功的promise函数,函数内部也返回了相应的结果值
接着我们再对axios调用then方法,打印结果看看
打印结果:
结果显示与axios执行的结果值一致。这样一来我们就实现了预期的目标。
总结
今天我们完成了利用自己创建的axios来发送请求获取数据,其中涉及到一些promise函数的知识点,而在后面的文章中,我们要更多的去使用promise函数完成业务逻辑,因此在学习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>
// 创建Axios构造函数
function Axios(config) {
this.default = config,
this.interceptors = {
request: '',
response: ''
}
}
// 挂载request函数并书写相关逻辑业务
Axios.prototype.request = function (config) {
// 创建一个成功的promise
let promise = Promise.resolve(config)
// 创建chains数组
let chains = [dispatchRequest, undefined]
let result = promise.then(chains[0], chains[1])
return result
}
// 创建dispatchRequest函数
function dispatchRequest(config) {
return xhrAdapter(config).then(res => {
return res
}, err => {
return err
})
}
// 创建xhrAdapter函数
function xhrAdapter(config) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open(config.method, config.url)
xhr.send()
xhr.onreadystatechange = function (config) {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve({
//配置对象
config: config,
//响应体
data: xhr.response,
//响应头
headers: xhr.getAllResponseHeaders(), //字符串 parseHeaders
// xhr 请求对象
request: xhr,
//响应状态码
status: xhr.status,
//响应状态字符串
statusText: xhr.statusText
});
} else {
//失败的状态
reject(new Error('请求失败 失败的状态码为' + xhr.status));
}
}
}
})
}
let axios = Axios.prototype.request.bind(null);
axios({
url: 'http://localhost:3000/posts',
method: 'GET'
}).then(res => {
console.log(res)
})
</script>
</body>
</html>