用axios和后端接口进行数据交互,那么axios内部实现原理到底是什么样的

axios源码学习

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js中进行使用。github地址

之前就粗略地阅读了axios的一些核心源码,最近闲来无事,就对axios的构建过程及重要特性的实现又进一步地去阅读,毕竟是吃饭的家伙嘛,还是要做到心里有数的。axios的常见用法几相关特性就不在这里一一罗列了,不清楚的同学可以先移步到这里。篇幅有限,node环境相关的学习,大家感兴趣的可以进一步学习。

源码目录

我们分析的源码都在/lib这个大目录下,目录如下。

adaapters 适配器(适配浏览器环境和node环境)

cancel 取消请求类(定义请求取消的类)

core 核心功能模块 (请求定义及实现的核心逻辑)

helper (辅助功能模块)

axiox.js(axios导出的文件,入口文件)

default.js (axios的默认配置文件)

utils (常用的工具类方法)

axios工作流程图

开局一张图,剩下全靠编。 (浏览器环境)

axios工作原理解析

我们先不关注axios的一些扩展方法,先理清axios是如何进行工作的,我们先找到入口文件axios.js

varbind =require('./helpers/bind');// 绑定上下文函数varAxios =require('./core/Axios');// 核心代码入口varmergeConfig =require('./core/mergeConfig');// 合并对象vardefaults =require('./defaults');// 默认配置文件functioncreateInstance(defaultConfig){// 创建axios对象实例varcontext =newAxios(defaultConfig);varinstance = bind(Axios.prototype.request, context);  utils.extend(instance, Axios.prototype, context);  utils.extend(instance, context);returninstance;}// Create the default instance to be exportedvaraxios = createInstance(defaults);modules.export.default axios复制代码

我们可以看到,我们一直使用的axios是通过createInstance这个方法返回的

createInstance里的返回的axios函数,是对Axios这个核心类生成的context实例进行处理后返回的

有两个关键的工具函数extend和bind,对context实例进行处理

extend函数functionextend(a, b, thisArg){  forEach(b,functionassignValue(val, key){if(thisArg &&typeofval ==='function') {      a[key] = bind(val, thisArg);    }else{// 将b对象上的key value, 赋值到a对象上a[key] = val;    }  });returna;}bind函数 基本于原生js的bind方法没有区别,其实就是返回Axios.prototype.request函数functionbind(fn, thisArg){returnfunctionwrap(){varargs =newArray(arguments.length);for(vari =0; i < args.length; i++) {      args[i] =arguments[i];    }returnfn.apply(thisArg, args);  };}复制代码

这么一顿骚操作后,在createInstance返回的axios函数的属性上,就已经挂载了Axios原型上的方法和属性了。

核心类Axios

Axios类的实现在core/Axios.js中,

functionAxios(instanceConfig){this.defaults = instanceConfig;this.interceptors = {// 拦截器request:newInterceptorManager(),response:newInterceptorManager()  };}Axios.prototype.request =functionrequest(config){// Allow for axios('example/url'[, config]) a la fetch APIif(typeofconfig ==='string') {    config =arguments[1] || {};    config.url =arguments[0];  }else{    config = config || {};  }// 合并默认配置和用户自定义配置config = mergeConfig(this.defaults, config);if(config.method) {    config.method = config.method.toLowerCase();  }elseif(this.defaults.method) {    config.method =this.defaults.method.toLowerCase();  }else{    config.method ='get';  }// 定义promise链varchain = [dispatchRequest,undefined];varpromise =Promise.resolve(config);// 请求拦截器是向前插入的,所以拦截器函数执行的顺序是倒序的this.interceptors.request.forEach(functionunshiftRequestInterceptors(interceptor){    chain.unshift(interceptor.fulfilled, interceptor.rejected);  });// 相应拦截器是向后插入的,所以拦截器函数执行的顺序是正续的this.interceptors.response.forEach(functionpushResponseInterceptors(interceptor){    chain.push(interceptor.fulfilled, interceptor.rejected);  });// 当且仅但chain链的为空时while(chain.length) {    promise = promise.then(chain.shift(), chain.shift());  }// 最后返回一个promisereturnpromise;};复制代码

我们看下这个Axios实例化跟request函数做了什么事情。

首先对默认的config和自定义的config进行了合并处理。

接着定义了promise链chain数组,并且通过promise.resolve(config)生成一个新的promise,接着对请求拦截器和相应拦截器插入到chain数组中。

判断chain数组长度,如果length不等于0,不断的更新promise的值,最后返回最终的promise。

这也是为什么我们可以链式调用的axios的原因,因为最终返回的是一个promise对象。接着我们来一一对其中的重要的组成部分进行分析。

dispatchRequest

代码在core/dispatchRequest.js下,这里我们先不关注请求取消的逻辑,只看请求发送的逻辑部分

module.exports =functiondispatchRequest(config){// 判断请求是否取消throwIfCancellationRequested(config);// Ensure headers existconfig.headers = config.headers || {};// Transform request dataconfig.data = transformData(    config.data,    config.headers,    config.transformRequest  );// Flatten headersconfig.headers = utils.merge(    config.headers.common || {},    config.headers[config.method] || {},    config.headers  );  utils.forEach(    ['delete','get','head','post','put','patch','common'],functioncleanHeaderConfig(method){deleteconfig.headers[method];    }  );varadapter = config.adapter || defaults.adapter;returnadapter(config).then(functiononAdapterResolution(response){    throwIfCancellationRequested(config);// Transform response dataresponse.data = transformData(      response.data,      response.headers,      config.transformResponse    );returnresponse;  },functiononAdapterRejection(reason){if(!isCancel(reason)) {      throwIfCancellationRequested(config);// Transform response dataif(reason && reason.response) {        reason.response.data = transformData(          reason.response.data,          reason.response.headers,          config.transformResponse        );      }    }returnPromise.reject(reason);  });};复制代码

dispatchRequest函数的逻辑非常清晰明了

首先会对通过transformData对config的data、headers、transformRequest属性进行处理,这里也验证了流程图汇总,在发送xhr前,会对confgi进行transformData的处理

transform函数varutils =require('./../utils');module.exports =functiontransformData(data, headers, fns){/*eslint no-param-reassign:0*/utils.forEach(fns,functiontransform(fn){    data = fn(data, headers);  });returndata;};复制代码

可以从代码中看出传入的fns其实是一个函数数组,循环遍历函数数组,将当前的data和headers属性当作参数传入每一个fn中,并将返回值作为新的data的值,从而达到对config.data的多次处理,从官方文档中也可以看到这样的用法

// `transformRequest` 允许在向服务器发送前,修改请求数据// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 StreamtransformRequest: [function(data, headers){// 对 data 进行任意转换处理returndata;  }],// `transformResponse` 在传递给 then/catch 前,允许修改响应数据transformResponse: [function(data){// 对 data 进行任意转换处理returndata;  }],复制代码

对config.headers字段进行了深度合并,接着将headers上绑定的请求方法名遍历循环删除掉

通过config.adpater来确定是node环境还是浏览器环境,从而决定是调用node.js的http模块方法还是浏览的XHR方法。在默认的配置文件中core/default.js

functiongetDefaultAdapter(){varadapter;if(typeofXMLHttpRequest !=='undefined') {// For browsers use XHR adapteradapter =require('./adapters/xhr');  }elseif(typeofprocess !=='undefined'&&Object.prototype.toString.call(process) ==='[object process]') {// For node use HTTP adapteradapter =require('./adapters/http');  }returnadapter;}复制代码

通过getDefaultadapter方法,去引用不同的文件,浏览器环境的话就直接走到

adapter =require('./adapters/xhr');复制代码

最后会对请求的返回的响应结果进行transofromData的处理,与上面处理请求参数的原理相似。

xhr

...

文章未完,详情请访问地址:http://www.msedt.com/infoflow/details/1053面试一点通

你可能感兴趣的:(用axios和后端接口进行数据交互,那么axios内部实现原理到底是什么样的)