前言
说到 JS HTTP 请求,就不得不提 Axios,作为前端网络请求库领域中的霸主,被广泛应用于众多的 web 项目中。
几款热门 HTTP 请求库在 GitHub 上的受欢迎程度
热门 JS HTTP 请求库 | 特性简介 | Star | Fork |
---|---|---|---|
Axios | 基于 Promise,支持浏览器和 node | 85.4k | 8.3k |
Request | 不基于 Promise,简化版的 HTTP | 25.2k | 3.1k |
Fetch | 基于 Promise,不支持 node 调用 | 24.8k | 3k |
Superagent | 15.7k | 1.3k |
虽然大家都是对 XMLHttpRequest 的封装,但是纵观 Axios 的热度,一骑绝尘啊!由此可见,Axios 真的是一个很优秀的开源项目。然而惭愧的是日常开发中总是拿来就用,一直没有静下心来好好拜读一番 Axios 的源码,会不会有很多人跟我一样呢?这里先列举一下 axios 项目的核心目录结构:
lib
└─ adapters
├─ http.js // node 环境下利用 http 模块发起请求
├─ xhr.js // 浏览器环境下利用 xhr 发起请求
└─ cancel
├─ Cancel.js
├─ CancelToken.js
├─ isCancel.js
└─ core
├─ Axios.js // 生成 Axios 实例
├─ InterceptorManager.js // 拦截器
├─ dispatchRequest.js // 调用适配器发起请求
...
└─ helpers
├─ mergeConfig.js // 合并配置
├─ ...
├─ axios.js // 入口文件
├─ defaults.js // axios 默认配置项
├─ utils.js
简介
Axios 是一个基于 Promise 网络请求库,作用于 node.js 和浏览器中。在服务端它使用原生 node.jshttp
模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。特性:
-
从浏览器创建XMLHttpRequests
-
从 node.js 创建http请求
-
支持PromiseAPI
-
拦截请求和响应
-
转换请求和响应数据
-
取消请求
-
自动转换 JSON 数据
-
客户端支持防御XSRF
Axios 内部运作流程
接下来我们结合 axios 的运作流程一起来剖析以下几个模块:
- Axios 构造函数
- 请求 / 响应拦截器
- dispatchRequest 派发请求
- 转换请求 / 响应数据
- 适配器处理 HTTP 请求
Axios 如何支持不同的使用方式?
使用 axios 发起请求
我们先来回忆一下平时是如何使用 axios 的:
// 方式 1 axios(config)
axios({
method: 'get',
url: 'xxx',
data: {
}
});
// 方式 2 axios(url[, config]),默认 get 请求
axios('http://xxx');
// 方式 3 使用别名进行请求
axios.request(config)
axios.get(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
...
// 方式 4 创建 axios 实例,自定义配置
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {
'X-Custom-Header': 'foobar'}
});
axios#request(config)
axios#get(url[, config])
axios#post(url[, data[, config]])
axios#put(url[, data[, config]])
...
源码分析
首先来看 axios 的入口文件, lib 目录下的axios.js
:
// /lib/axios.js
function createInstance(defaultConfig) {
// 创建 axios 实例
var context = new Axios(defaultConfig);
// 把 instance 指向 Axios.prototype.request 方法
var instance = bind(Axios.prototype.request, context);
// 把 Axios.prototype 上的方法扩展到 instance 上,指定上下文是 context
utils.extend(instance, Axios.prototype, context);
// 把 context 上的方法扩展到 instance 上
utils.extend(instance, context);
// 导出 instance 对象
return instance;
}
var axios = createInstance(defaults);
// 添加 create 方法,返回 createInstance 函数,参数为自定义配置 + 默认配置
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
...
module.exports = axios;
// Allow use of default import syntax in TypeScript
module.exports.default = axios;
可见,当我们调用axios()
时,实际上是执行了createInstance
返回的一个指向Axios.prototype.request
的函数;通过添加create
方法支持用户自定义配置创建,并且最终也是执行了Axios.prototype.request
方法;接下来我们看看Axios.prototyp