# axios-note **Repository Path**: lafen/axios-note ## Basic Information - **Project Name**: axios-note - **Description**: axios 源码注释 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-10 - **Last Updated**: 2022-10-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: Axios ## README # axios axios 源码注释 ### 1、axios 的多种使用方式 ```js // 引入 import axios from 'axios' ``` 1. 第一种 axios(option) ```js axios({ url, method, headers, }) ``` 2. 第二种 axios(url[, option]) ```js axios(url, { method, headers, }) ``` 3. 第三种 axios.get(url[, option]) 等 ```js axios.get(url, { headers, }) ``` 4. 第四种 axios.post(url[, data[, option]]) ```js axios.post(url, data, { headers, }) ``` 5. 第五种 axios.request(option) ```js axios.request({ url, method, headers, }) ``` axios 为何能使用这么多种方式使用?看 import 导入的实例 axios 从 axios.js 来,打开源文件看对 axios 实例做了什么,其中的核心是 createInstance ```js /** * Create an instance of Axios * * @param {Object} defaultConfig The default config for the instance * @return {Axios} A new instance of Axios */ function createInstance(defaultConfig) { // context 实例 var context = new Axios(defaultConfig); // 将 instance 指向 Axios.prototype.request,并绑定上下文为 context(axios实例) var instance = bind(Axios.prototype.request, context); // 将 Axios.prototype 上的 get、post、delete 等方法拷贝到 instance 上,并指定上下文为 context utils.extend(instance, Axios.prototype, context); // 将 defaults 默认配置和 interceptors 拦截器 拷贝给 instance utils.extend(instance, context); return instance; } var axios = createInstance(defaults); ``` 可以看出 createInstance 最终返回了一个 Function,这个 Function 指向 Axios.prototype.request,该函数还会有 Axios.prototype 上的每个方法作为静态方法,且上下文指定为一个 axios 实例。 那去看看 Axios 类中实例化了什么, ```js /** * Create a new instance of Axios * * @param {Object} instanceConfig The default config for the instance */ function Axios(instanceConfig) { this.defaults = instanceConfig; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; } /** * Dispatch a request * * @param {Object} config The config specific for this request (merged with this.defaults) */ Axios.prototype.request = function request(config) { // ...ignore code }; utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, config) { return this.request(mergeConfig(config || {}, { method: method, url: url, data: (config || {}).data })); }; }); utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, data, config) { return this.request(mergeConfig(config || {}, { method: method, url: url, data: data })); }; }); ``` Axios 实例化时初始化了一下默认配置和拦截器,往下的 Axios.prototype.request 则给 Axios 原型挂载了核心方法 request,下面的两个 utils.forEach 则给 Axios 原型上继续追加了 get、post 等别名方法,实际上他们内部调用的都是原型上的 request 方法。至此我们能够通过多种方法发起 http 请求了。 ### 2、request 方法 所有的请求都从 request 发出,看看里面做了什么处理 ```js /** * Dispatch a request * * @param {Object} config The config specific for this request (merged with this.defaults) */ Axios.prototype.request = function request(config) { /*eslint no-param-reassign:0*/ // Allow for axios('example/url'[, config]) a la fetch API if (typeof config === 'string') { config = arguments[1] || {}; config.url = arguments[0]; } else { config = config || {}; } config = mergeConfig(this.defaults, config); // Set config.method if (config.method) { config.method = config.method.toLowerCase(); } else if (this.defaults.method) { config.method = this.defaults.method.toLowerCase(); } else { config.method = 'get'; } // Hook up interceptors middleware var chain = [dispatchRequest, undefined]; var promise = Promise.resolve(config); this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); }); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; }; ``` request 方法中: 1. 首先对 config 做了处理,兼容 axios(option) 和 axios(url, option) 调用。 2. 将传入的 config 与默认配置覆盖混合,传入的配置优先级高于默认配置。 3. 然后修正 config.methods 为小写。 4. 声明 chain 数组为 [dispatchRequest, undefined],在 chain 数组左边 unshift 请求拦截器,右边 push 响应拦截器。 5. 通过 while 遍历 chain,将数组元素两两 shift 出来给 promise 的成功与失败函数,返回 promise ### 3、请求拦截器、响应拦截器怎么处理的 先看使用 ```js // 请求拦截 const requestInterceptor = axios.interceptors.request.use( config => { return config; // 处理后必须返回 config 对象 }, error => { return Promise.reject(error); } ); // 响应拦截 axios.interceptors.response.use( response => { return response; // 处理后必须返回 response 对象 }, error => { return Promise.reject(error); } ); // 取消某个拦截器 axios.interceptors.request.eject(requestInterceptor); ``` 实际上 use 传入的两个函数存入拦截器中的 handlers 数组中,与 chain.unshift 和 chain.push 对应 ```js InterceptorManager.prototype.use = function use(fulfilled, rejected) { this.handlers.push({ fulfilled: fulfilled, rejected: rejected }); return this.handlers.length - 1; // 返回 索引id 方便 eject 时取消 }; ```