一比一还原axios源码(四)—— Axios类
axios源码的分析,到目前为止,算上第0章已经四章了,但是实际上,还都没有进入axios真正的主线,我们来简单回顾下。最开始我们构建了get请求,写了重要的buildURL方法,然后我们处理请求体请求头,响应体响应头,这样我们就可以传json对象了,然后还加入了promise,让我们可以链式点用,最后还加了错误处理,让我们可以更好的操作请求信息。
但是,大家发现了没有,目前为止我们所写的核心其实就是一个XMLHttpRequest对象,所有的内容都围绕着这个对象。代码也没有做太清晰的分割,那么今天,我们就来完成axios的核心主题,也就是Axios类,有了这个,大家就可以通过一些直观的方法来快速的调用axios的请求API了。
依照惯例,从axios的API入手,我们今天要实现的内容如下:
那么接下来我们就进入正题吧。
首先,我们在core文件夹下创建一个Axios文件。声明一个Axios类:
export default function Axios(config) {}
这个axios很简单,我们暂时这样,什么都不需要。然后我们在Axios的原型上挂载一个request方法,这个方法是真正的请求方法,也就是说,所有的axios请求,其实都是request。
Axios.prototype.request = function (url, config) {
if (typeof url === "string") {
if (!config) {
config = {};
}
config.url = url;
} else {
config = url;
}
return dispatchRequest(config);
};
首先我们来看下,request方法实际上有两个核心,一个是参数的重载,听起来很高大上,实际上就是可以传一个参数,也可以把url单独抽离出来作为参数及其他的config来传递,最重要的就是dispatchRequest,上面说所有的axios的请求都是request,那么其实request,就是dispatchRequest。我们来看下,怎么搞出来的dispatchRequest。
还记不记得之前lib根目录下的axios,没错,把里面的代码复制过来就可以了。那axios就没东西了,我们改下axios里的代码:
import Axios from "./core/Axios";
import bind from "./helpers/bind";
import utils from "./utils";
/**
* Create an instance of Axios
*
* @param {Object} defaultConfig The default config for the instance
* @return {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context); // Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context); // Copy context to instance
utils.extend(instance, context); return instance;
} // Create the default instance to be exported
var axios = createInstance(); // Expose Axios class to allow class inheritance
axios.Axios = Axios; export default axios;
诶?怎么代码风格变了?好吧,我承认这是从axios源码复制过来的,毛都没改,就改了改引用。然后呢,这个createInstance实际上就是个工厂函数。创建并返回axios的实例。我们暂时不看extend和bind具体的源码,从字面意思来看,instance实例上绑定request方法,也就是说,我可以直接使用axios.request。extend就是把某些东西,也就是复制了属性到实例上。OK,到此,核心的axios体系基本上完成了。但是我们还漏了一个很重要的事情,就是本章最开始的调用方式,我们希望可以在实例上直接调用get、post等方法。那么我们来看下代码:
// Provide aliases for supported request methods
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,
})
);
};
});
上面把方法分成了两类,一类是有body的,一类是没有的。然后调用request,把参数传进去就好了,简单的一批。至于mergeConfig方法,咱们稍后面再说。我会尽可能的把他们都注释一遍,可以去源码里查阅,因为这些东西都差不多可以拆出来,单独使用,不在axios的核心线上,utils是单纯的工具,与业务无关,而helpers包含了对业务的一定的抽象和关联。到这里,我们就可以使用axiso.get这样的方法来调用接口了。
那,额外的,我们来分析下bind、extend和mergeConfig方法:
1.bind
我们先来看下代码:
export default function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
}
咱们先来字面看一下,传入了一个fn和thisArg参数,然后返回了一个wrap函数。wrap里面根据wrap的arguments长度创建了个数组,然后挨个的把arguments的参数复制给args,然后再返回一个fn.apply。所以,字面意思咱们理解了,但是它到底干了啥呢?我们就拿用到了它的地方做个举例:
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
那怎们把参数传进去,其实他就是:
return Axios.prototype.request.apply(context, args);
解释下这句话吧,就是request方法的this只想context,也就是new Axios(defaultConfig),然后把args作为参数传进去,那args就是传给wrap的参数。那我们再写个例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script>
function bind(fn, thisArg) {
return function wrap() {
console.log(arguments);
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
} function A(config) {
console.log(config);
}
A.prototype.request = function () {
console.log("request");
};
var context = new A({ a: 2, b: 3 });
var instance = bind(A.prototype.request, context);
instance(1, 2, 3);
</script>
</html>
大家捋一下哦。我们再回到这块代码:
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context); // Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context); // Copy context to instance
utils.extend(instance, context); return instance;
}
最后,我们返回了instance,对吧?那么就意味着我们可以这样用:
instance({
method:'post',
// xxxxxx
})
那我有个问题,最终这个传递的参数给了谁?如果不知道答案的话,那就再回头看一遍吧~~~。
2.extend
extend方法,说白了,就是把a的属性,复制给b没了。我们来看下代码:
function extend(a, b, thisArg) {
forEach(b, function assignValue(val, key) {
if (thisArg && typeof val === "function") {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
});
return a;
}
我们来看下,字面意思的话,就是,如果有thisArg,并且某一个b中的属性是一个函数,那么a中对应的key就是bind后的函数,否则就是单纯的复制。简单吧~~~。那这里我就不说bind是咋回事了啊,看不懂回头看啊。咱们这叫一步三回头。:)
3.mergeConfig
这个呢,看起来复杂, 说起来简单,因为篇幅较长,我就不在这里说了,大家自己去项目中对应的分支看注释哦。但是我简单说下,这个mergeConfig实际上使用了一种策略模式,简单点说其实就是根据不同的对象,来分配不同的合并方法。一共有那么1、2、3、4、5,哦对,四种合并策略(去看了源码你就知道我这里没说错了,我扩起来说是怕你骂我,你骂我倒无所谓,我怕你骂错了,嘻嘻)。
我们再来回顾下,今天的核心主线:
- 创建Axios类。
- 在Axios的原型上扩展核心request方法。
- 扩展其他alias方法,内部就是调用的request。
- 创建dispatchRequest,是request方法的核心(就是之前旧的lib/axios里的代码)。
- 创建createInstance工厂函数,绑定数据到实例上。
- 解释了bind、extend方法的含义,mergeConfig自己去代码看。
今天就做了这些,其实不复杂,跟着我,带你一比一还原axios(其实就是教你怎么抄)。额……咳咳……读书人的事,怎么能叫做抄呢~~我们下一章子再抄,哦不,再借鉴噢。
一比一还原axios源码(四)—— Axios类的更多相关文章
- 一比一还原axios源码(零)—— 概要
从vue2版本开始,vue-resource就不再被vue所维护和支持,官方也推荐使用axios,所以,从我使用axios至今,差不多有四五年了,这四五年的时间只能算是熟练应用,很多内部的实现和原理不 ...
- 一比一还原axios源码(一)—— 发起第一个请求
上一篇文章,我们简单介绍了XMLHttpRequest及其他可以发起AJAX请求的API,那部分大家有兴趣可以自己去扩展学习.另外,简单介绍了怎么去读以及我会怎么写这个系列的文章,那么下面就开始真正的 ...
- 一比一还原axios源码(三)—— 错误处理
前面的章节我们已经可以正确的处理正确的请求,并且通过处理header.body,以及加入了promise,让我们的代码更像axios了.这一章我们一起来处理ajax请求中的错误. 一.错误处理 首先我 ...
- Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主
前戏 在正式开始axios讲解前,让我们先想想,如何对现有的$.ajax进行简单的封装,就可以直接使用原声Promise了? let axios = function(config){ return ...
- Axios源码分析
Axios是一个基于promise的HTTP库,可以用在浏览器和node.js中. 文档地址:https://github.com/axios/axios axios理解和使用 1.请求配置 { // ...
- Axios源码深度剖析
Axios源码深度剖析 - XHR篇 axios 是一个基于 Promise 的http请求库,可以用在浏览器和node.js中,目前在github上有 42K 的star数 分析axios - 目录 ...
- axios源码解析 - 请求拦截器
axios请求拦截器,也就是在请求发送之前执行自定义的函数. axios源码版本 - ^0.27.2 (源码是精简版) 平时在业务中会这样去写请求拦截器,代码如下: // 创建一个新的实例 var s ...
- JDK源码之Integer类分析
一 简介 Integer是int基本类型的包装类,同样继承了Number类,实现了Comparable接口,String类中的一些转化方法就使用了Integer类中的一些API,且fianl修饰不可继 ...
- Struts2 源码分析——Result类实例
本章简言 上一章笔者讲到关于DefaultActionInvocation类执行action的相关知识.我们清楚的知道在执行action类实例之后会相关处理返回的结果.而这章笔者将对处理结果相关的内容 ...
随机推荐
- 分配IP地址的好东西 DHCP以及NAT简单介绍
主机配置协议DHCP 1.DHCP应用场景 2.DHCP基础原理 3.NAT简单介绍 4.配置命令 1.手工配置IP地址,工作量比较大而且不好管理,如果用户自己修改参数,可能会导致ip地址冲突,这个时 ...
- pyrealsense2学习
如何得到realsense设备信息 前提:将D455连接在电脑上,并且已经下载好 Realsense Viewer 打开Realsense Viewer--> Info, 便可得到相机的一些参数 ...
- postman项目接口文档和登录步骤原理
培训内容 实训项目:非常果岭-发现模块接口测试,单接口.流程脚本编写: 使用工具:postman 培训方式 1)postman使用说明 2)项目接口文档和登录步骤原理 一.首先了解postman使 ...
- 帆软报表(finereport)雷达图钻取详细点新页面展示
添加参数栏,季度下拉框的空间名为combobox0 添加雷达图,通过第三页面做跳转 雷达图钻取.cpt为联动钻取的第三页面 添加纬度(所点击钻取的点) 参数 wd 添加季度参数 jd 值为季 ...
- PHP+mysql真题
PHP+mysql真题 来自<PHP程序员面试笔试宝典>,涵盖了近三年了各大型企业常考的PHP面试题,针对面试题提取出来各种面试知识也涵盖在了本书. [真题215] 按要求写出SQL实现. ...
- 《PHP程序员面试笔试宝典》——什么是职场暗语?
本文摘自<PHP程序员面试笔试宝典> 文末有该书电子版下载. 随着求职大势的变迁发展,以往常规的面试套路因为过于单调.简明,已经被众多"面试达人"们挖掘出了各种&quo ...
- InfluxDB 2.x Open Source Time Series Database
1. 说明 目前,大家普遍还在采用 InfluxDB 1.x 的版本,官方2.x的版本已经发布一段时间了, 其主推flux语言且自带前端炫酷图表. 2. 官方网站 https://www.influx ...
- Linux性能优化概述
一.Linux性能概述 性能优化是个系统工程,总是牵一发而动全身,它涉及了从程序设计.编程语言,再到系统.存储.网络等各种底层基础设施的方方面面.每一个组件都有可能出问题,而且很有可能多个组件同时出问 ...
- SpringBoot外部配置属性注入
一.命令行参数配置 Spring Boot可以是基于jar包运行的,打成jar包的程序可以直接通过下面命令运行: java -jar xx.jar 那么就可以通过命令行改变相关配置参数.例如默认tom ...
- FSAF:嵌入anchor-free分支来指导acnhor-based算法训练 | CVPR2019
FSAF深入地分析FPN层在训练时的选择问题,以超简单的anchor-free分支形式嵌入原网络,几乎对速度没有影响,可更准确的选择最优的FPN层,带来不错的精度提升 来源:晓飞的算法工程笔记 公 ...