文章目录
- 一、模块化目录介绍
- 二、创建 axios 函数对象
- 1、创建 axios.js 文件
- 2、创建 defaults.js 文件
- 3、创建 _Axios.js 文件
- 4、总结
当前篇章正式进入手写 axios 源码系列,我们要真枪实弹的开始写代码了。
因为 axios 源码的代码量比较庞大,所以我们这里只抓主线,让大家对 axios 的主要核心功能有个大概了解,至于具体的细节深入,需要大家去通读 axios 源码了解。手写 axios 源码系列只将其中的4个主要功能模块进行了手写,就是在上篇文章: 手写axios源码系列一:axios核心知识点 中介绍的四个核心功能点:
- axios 函数对象
- dispatchRequest 发送请求
- interceptors 拦截器
- cancelToken 取消请求
一、模块化目录介绍
建议大家先下载 axios 的源码,然后过一遍源码的目录文件,主要文件全部在 lib
目录下,这里我就不一一介绍了。
接下来看一下我手写 axios 源码所创建的目录介绍(文件名称依据源码文件名称命名):
- _Axios.js:axios的构造函数,因为有一个 axios.js,文件名不区分大小写会重复,所以添加了下横杠
_Axios
防止文件名重复;其中 request 方法为重点,拦截器功能在这里书写; - adapters.js:适配器,其中有一个方法
getAdapter
来获取发送请求的方式 xhr 或者 http ; - axios.js:整个目录的入口文件,创建了axios 函数对象以及对外暴露了一些属性以及方法;
- CancelToken:取消请求的功能代码;
- defaults.js:默认配置项(预配置);
- dispatchRequest.js:真正发送请求以及接收响应数据的文件目录;
- index.html:手写axios源码的测试代码,测试一下自己写的代码是否正确;
- InterceptorManager:拦截器的构造函数;
- xhr.js:书写 AJAX(Asynchronous Javascript And XML) 代码的文件目录。
二、创建 axios 函数对象
1、创建 axios.js 文件
import Axios from "./_Axios.js";
import defaults from "./defaults.js";
import CancelToken from "./CancelToken";
// 初始化 axios 函数对象
function createInstance(defaultConfig){
// 生成 _Aixos 的实例对象
const context = new Axios(defaultConfig);
// 生成 instance 绑定函数,并且绑定 this 为 context,防止调用时 this 指向不明
const instance = Axios.prototype.request.bind(context, ...arguments);
// 将 context 实例对象的属性复制到 instance 上
Object.keys(context).forEach(key=>{
instance[key] = context[key]
})
// 将 Axios 原型对象的属性复制到 instance 上
Object.keys(Axios.prototype).forEach(key=>{
instance[key] = Axios.prototype[key]
})
// axios.create 方法
instance.create = function(instanceConfig){
const mergeConfig = {
...defaultConfig,
...instanceConfig
}
return createInstance(mergeConfig);
}
// instance 就是 axios
return instance;
}
// 创建 axios 函数对象
const axios = createInstance(defaults);
// 对外暴露 CancelToken 类,取消请求时使用
axios.CancelToken = CancelToken;
// 导出 axios
export default axios;
2、创建 defaults.js 文件
export default {
// 适配器的默认配置,写适配器 adapters 代码时需要传入的配置
adapter: ['xhr', 'http']
}
3、创建 _Axios.js 文件
import InterceptorManager from "./IntercptorManager.js";
import dispatchRequest from "./dispatchRequest.js";
export default class Axios {
constructor(config){
// 实例对象的 defaults属性(默认配置对象)
this.defaults = config;
// 实例对象的 interceptors属性(拦截器)
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
// request 发送请求
request(configOrUrl, config){
if(typeof configOrUrl === "string"){
config.url = configOrUrl
} else {
config = {
...configOrUrl,
...config
}
}
// 将 config包装为一个 promise对象
let promise = Promise.resolve(config);
// 创建执行链,这里调用 dispatchRequest方法以发送请求
const chain = [dispatchRequest, undefined];
// 调用 promise.then()将 config传入 dispatchRequest中,dispatchRequest中返回的也是一个 prommise对象
promise = promise.then(chain[0], chain[1]);
// 发送请求后返回的是一个 promise对象
return promise;
}
}
const methodsWithNoData = ["delete", "get", "head", "options"];
const methodsWithData = ["post", "put", "patch"];
// 给 Axios的原型对象添加请求方法
methodsWithNoData.forEach(method => {
Axios.prototype[method] = function(url, config){
config = {
...config,
method,
url
}
// 最后返回 request生成的 promise对象
return this.request(config)
}
})
methodsWithData.forEach(method => {
Axios.prototype[method] = function(url, data, config){
config = {
...config,
method,
url,
data
}
return this.request(config)
}
})
4、总结
以上代码包含了 axios 函数对象的创建过程:
- 使用
createInstance()
生成 axios 函数对象- 先实例化对象 context,然后使用 Axios.prototype.request 方法 bind 一个新函数 instance,调用
instance()
就相当于调用Axios.prototype.request()
; - 生成的新函数 instance 没有任何属性以及方法可以使用,所以将 context 以及 Axios.prototype 上的属性以及方法复制给 instance 使用;
- 挂载一个 create 方法,供 axios 封装使用,调用
create()
方法其实就是调用createInstance()
方法。
- 先实例化对象 context,然后使用 Axios.prototype.request 方法 bind 一个新函数 instance,调用
- 为 axios 挂载其他属性,对外暴露以供使用。
- 导出 axios 函数对象。
下篇文章我们着重讲解发送请求 dispatchRequest 以及适配器 adapters 。