axios中的那些天才代码!看完我实力大涨!

科技   2024-08-27 18:24   广东  

经常调接口的同学一定非常熟悉aixos下面的两种使用方式:

  • axios(config)
// 配置式请求``axios({`` ``method: ``'post'``,`` ``url: ``'/user/12345'``,``});
  • axios.post(url, config)
// 简洁的写法``axios.post(``'/user/12345'``)

不知道各位大佬有没有思考过这样的问题:

axios到底是个什么东西?我们为什么可以使用这两种方式请求接口呢?axios是怎么设计的?

axios原理简析

为了搞明白上面的问题,我们先按照传统思路仿照axios源码实现一个简单的axios。

unsetunset手写一个简单的axiosunsetunset

创建一个构造函数

function Axios(config){``  ``this``.defaults = config;    ``// 配置对象``  ``this``.interceptors = {     ``// 拦截器对象``   ``request:{},``   ``response:{}`` ``}``}

上面的代码中,我们实现了一个基本的Axios类,但它还不具备任何功能。我们现在给它添加功能。

原型上添加方法

Axios.prototype.request = function(config){``    ``console.log(``'发送Ajax 请求 type:'` `+config.method)``}``Axios.prototype.``get` `= function(){``    ``return` `this``.request({method:``'GET'``})``}``Axios.prototype.post = function(){``    ``return` `this``.request({method: ``'POST'``})``}

上面的代码中,我们在request属性上创建了一个通用的接口请求方法,get和post实际都调用了request,但内部传递了不同的参数,这和axios(config)、axios.post()有异曲同工之妙。

参考aixos的用法, 现在,我们需要创建实例对象

let` `aixos = ``new` `Axios(config)

创建后的axios包含defaultsinterceptors属性,其对象原型__proto__上(指向Axios的prototype)包含request、get及post方法,因此,我们现在可以使用aixos.post()的方法模拟调用接口了。

但注意,此时aixos只是一个实例对象,不是一个函数!我们似乎也没办法做到改造代码使用aixos(config)的形式调用接口!

aixos是如何实现的呢?

unsetunsetaixos中的天才想法unsetunset

为了即能使用axios(config)又能使用axios.get(),axios的核心伪逻辑如下

function Axios(config){``  ``this``.defaults = config;    ``// 配置对象``  ``this``.interceptors = {     ``// 拦截器对象``   ``request:{},``   ``response:{}`` ``}``}` `Axios.prototype.request = function(config){``    ``console.log(``'发送Ajax 请求 type:'` `+config.method)``}``Axios.prototype.``get` `= function(){``    ``return` `this``.request({method:``'GET'``})``}``Axios.prototype.post = function(){``    ``return` `this``.request({method: ``'POST'``})``}` `function createInstance(config) {``  ``//注意instance是函数``  ``const` `instance = Axios.prototype.request;``  ``instance.``get` `= Axios.prototype.``get``  ``instance.post = Axios.prototype.post``  ``return` `instance;`` ``}` `let` `axios = createInstance();

通过上述的伪代码,我们可以知道axios是createInstance()函数的返回值instance

  • instance 是一个函数,因此,axios也是一个函数,可以使用axios(config);
  • instance也是一个对象(js万物皆对象),其原型上有get方法和post方法,因此,我们可以使用axios.post()。

我们看看aixos的源码

unsetunsetaixos的源码实现unsetunset

function createInstance(config) {`` ``//实例化一个对象`` ``var` `context = ``new` `Axios(config); ``//但是不能直接当函数使用`` ` ` ``var` `instance = Axios.prototype.request.bind(context);`` ``//instance 是一个函数,并且可以 instance({}),` ` ``//将Axios.prototype 对象中的方法添加到instance函数中,让instance拥有get、post、request等方法属性`` ``Object.keys(Axios.prototype).forEach(key => {``  ``// console.log(key); //修改this指向context``  ``instance[key] = Axios.prototype[key].bind(context);`` ``})`` ``//总结一下,到此instance自身即相当于Axios原型的request方法,`` ``//然后又给instance的属性添加了上了Axios原型的request、get、post方法属性`` ``//然后调用instance自身或instance的方法属性时,修改了this指向context这个Axios实例对象` ` ``//为instance函数对象添加属性 default 与 intercetors`` ``Object.keys(context).forEach(key => {``  ``instance[key] = context[key];`` ``})` ` ``return` `instance;``}

可以说,上面的代码真的写的精妙绝伦啊!

注意这里,为什么要修改this的指向

var` `instance = Axios.prototype.request.bind(context);

首先,requset 是Axios原型对象上的方法,其方法内部的this指向的是其实例化对象context!

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;``};

因此,如果我们直接使用Axios.prototype.request()就会出现问题,因为这事reques方法内部的this会指向错误,导致函数不能运行,因此,我们必须将this重新指向其实例化对象。

作者:林恒

https://www.cnblogs.com/smileZAZ/p/18330636


加我微信,拉你进前端进阶、面试交流群,互相监督学习进步等!

❤️ 看完三件事

如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  1. 点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

  2. 关注我的博客 https://github.com/qappleh/Interview,让我们成为长期关系

  3. 关注公众号「深圳湾码农」,持续为你推送精选好文,回复「加群」加入面试互助交流群

点一下,代码无 Bug

深圳湾码农
分享大前端最新技术、BAT大厂面试题、程序员轶事、职场成长等,你想要的这里都有.
 最新文章