前言
本文需对js、es6、webpack、网络请求等基础知识有基本了解
相信axios大家都用过或者什么其他的网络请求相关的库,什么ajax、fly.js等等等等,光我用过的请求库就七八个,都大同小异
本文并不是要完完全全一个字不差的实现axios所有功能,没有意义,但是也会实现的七七八八,主要是感受一下这个流程和架子、以及 这个项目怎么才能易于拓展、是不是易于测试的、可读性怎么样等等等等
废话不多说,开搞~
搭建项目
老规矩先建一个空目录,然后打开命令行执行
yarn init -y
或
cnpm init -y
webpack
然后是引入webpack,虽然本节不主讲webpack,这里我稍微提一嘴,webpack和webpack-cli不光项目里要下载,全局也要下载,也就是
yarn global add webpack webpack-cli
安装依赖包
执行命令,主要就需要这几个包,帮忙编译和调试的,babel帮助尽量兼容浏览器的,毕竟咱们写代码肯定充满了es6
yarn add webpack webpack-cli webpack-dev-server babel-loader @babel/core @babel/preset-env
配置webpack
接下来再在根目录创建webpack.config.js来配置一下webpack,然后再建一个src目录,来存放咱们这个库的代码,现在的目录就会是这个样子
先简单配置一下,后续有需求在加,这里就直接上代码了
~ webpack.config.js
const path = require('path');
module.exports = function() {
const dev = true;
return {
mode: dev ? 'development' : 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: dev ? 'axios.js' : 'axios.min.js',
sourceMapFilename: dev ? 'axios.map' : 'axios.min.map',
libraryTarget: 'umd',
},
devtool: 'source-map',
};
};
这时候在src里面,建一个index.js,然后随便写点东西,像这样
然后终端执行webpack命令
当然了,现在肯定是不兼容的,要不咱们一开始也不用下babel了,咱们可以试试,比如我现在index.js加一句话
然后编译完可以看到结果也还是let,这肯定不行
好的,那么接下来就是配置babel,没什么可说的,这里直接放代码了,没什么可说的
const path = require('path');
module.exports = function() {
const dev = true;
return {
mode: dev ? 'development' : 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: dev ? 'axios.js' : 'axios.min.js',
sourceMapFilename: dev ? 'axios.map' : 'axios.min.map',
libraryTarget: 'umd',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/i,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
],
},
};
};
然后,大家肯定不希望每次修改手动的去webpack一下对吧?把webpack-dev-server引进来
~ webpack.config.js
const path = require('path');
module.exports = function() {
const dev = true;
return {
mode: dev ? 'development' : 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: dev ? 'axios.js' : 'axios.min.js',
sourceMapFilename: dev ? 'axios.map' : 'axios.min.map',
libraryTarget: 'umd',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/i,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
],
},
devServer: {
port: 8000,
open: true,
},
};
};
这时候,直接终端里运行webpack-dev-server的话其实他会自动去找全局的模块,这样不好,所以。。。你懂的
直接package.json里加上命令
~ package.json
{
"name": "axios",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "webpack-dev-server"
},
"dependencies": {
"@babel/core": "^7.7.7",
"@babel/preset-env": "^7.7.7",
"babel-loader": "^8.0.6",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
}
}
然后yarn start
这时候会弹出一个html
当然了,默认是去找根下的index.html,咱们没有,所以在根下建一个,然后引入咱们的axios.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>axios</title>
</head>
<body>
<script src="/axios.js"></script>
</body>
</html>
刷新页面就会看到src/index.js里的alert生效了
并且 webpack-dev-server也是可以的,改了代码页面会自动刷新
然后,咱们就来配一下build
这里就不多废话了,直接上代码了
~ package.json
{
"name": "axios",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "webpack-dev-server --env.dev",
"build": "webpack --env.prod"
},
"dependencies": {
"@babel/core": "^7.7.7",
"@babel/preset-env": "^7.7.7",
"babel-loader": "^8.0.6",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
}
}
~ webpack.config.json
const path = require('path');
module.exports = function(env={}) {
const dev = env.dev;
return {
mode: dev ? 'development' : 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: dev ? 'axios.js' : 'axios.min.js',
sourceMapFilename: dev ? 'axios.map' : 'axios.min.map',
libraryTarget: 'umd',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/i,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
],
},
devServer: {
port: 8000,
open: true,
},
};
};
可以看到也是没问题的~
好的到这我本算是把webpack相关的东西搭的差不多了,接下来就要开始忙正事了~
Axios 项目代码
首先咱们先来建一个common.js,用来放公共的方法,先写一个断言
~ /src/common.js
export function assert(exp, msg = 'assert faild') {
if (!exp) {
throw new Error(msg);
}
}
然后再建一个文件(个人习惯)/src/axios 主要的文件放在这
然后大家来看看axios正经怎么用,是不是可以直接 axios({...})或者axios.get等等
在index.js引入直接写一下结果,把期望用法写上,然后来补充内部怎么写
~ index.js
import axios from './axios';
console.log(axios);
axios({ url: '1.txt', method: 'post' });
axios.get('1.txt', { headers: { aaa: 123 } });
axios.post(
'1.txt',
{ data: 123 },
{
headers: {
bbb: 123,
},
},
);
这时候就要考虑考虑了,咱们可以直接写函数,这个没问题,不过那样太散了,个人不喜欢,不过也是可以的,所以这里我就写成类了,由于改成类了那么输出出去的肯定得是一个实例,既然是实例的话,那么肯定也不能直接像函数一样直接()运行
没错,这时候就可以用到咱们的proxy了,js的class里的constructor里是可以return东西的,如果对这东西不太熟,建议先去看看js的class,这里就不多赘述,主要说明思想
简单来说,咱们可以return一个proxy对象,来代理咱们返回的结果,从而达到咱们既可以直接用class的方式写,用的时候也可以直接跟函数一样()调用
然后先来打印一下看看
~
这时候看页面的console,这时候可以看到axios就是一个proxy对象,像这样
这时候还能看到一个报错,因为咱们现在返回的是proxy对象,不是实例类了,没有get也是理所应当
可能有人会奇怪,为什么这个proxy监听的对象非要单独监听一个proxy函数呢,直接监听this不就行了么,注意,这其实是不行的,了解proxy的朋友应该知道,proxy你用什么监听创建的这事儿很重要,如果你监听的是一个对象,那还是不能直接调用,如果要是想直接像函数一样直接调用的话,那你监听的也必须是一个函数
像这样
然后咱们来解决一下get函数找不到的问题,来给proxy加一个方法, 很简单,可以给proxy加一个get方法,有人来找他,直接返回从我这个类上找,不就完了么,不过稍微要注意一下这个this,直接写this的话指向的是这个proxy
function request() {}
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
apply(fn, thisArg, args) {
console.log(fn, thisArg, args);
},
});
}
get() {
console.log('get');
}
post() {
console.log('post');
}
delete() {}
}
let axios = new Axios();
export default axios;
这时候再看,就没有报错了,并且get和post也能console出来
这时候咱们就可以接着写数据请求了。。。。。吗? 远远不够
axios的参数有很多的多样性,咱们想来大概总结一下
axios('1.txt',{})
axios({
url: '1.txt',
method
})
axios.get('1.txt')
axios.get('1.txt',{})
axios.post....
等等吧,这一点,怎么才能把这么复杂多源的参数统一处理
第二就是axios可以非常深度的定制参数,可以全局也可以单独定制,拦截器,transfrom什么的,等等吧,默认值等等
参数
首先先来一个default,来定义一下这些默认值,这里稍微说一下这个X-Request-By算是一个不成文的规范,这也是普遍请求库都愿意做的事,方便后台去判断你这个请求是来自ajax还是from还是浏览器的url
function request() {}
const _default = {
method: 'get',
headers: {
common: {
'X-Request-By': 'XMLHttpRequest',
},
get: {},
post: {},
delete: {},
},
};
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
apply(fn, thisArg, args) {
console.log(fn, thisArg, args);
},
});
}
get() {
console.log('get');
}
post() {
console.log('post');
}
delete() {}
}
let axios = new Axios();
export default axios;
当然了,这里图简单,简单写着几个参数,自己喜欢可以再加很多东西,比如data的默认值等等,先够用,后续不够用再加
这时候,咱们来思考一下,这个default要加给谁,直接axios.default = _default么,当然不是,因为咱们这个axios到最后肯定是需要axios.create多个实例的,那么这时候就不行了,互相影响,prototype就更不用说了
其实也很简单,每次创建的时候从_default复制一份新的就可以了,直接JSON.parse(JSON.stringify(_default))包一下就可以了,这也是性能最高的方式,然后稍微来改造一下代码
function request() {}
const _default = {
method: 'get',
headers: {
common: {
'X-Request-By': 'XMLHttpRequest',
},
get: {},
post: {},
delete: {},
},
};
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
apply(fn, thisArg, args) {
console.log(fn, thisArg, args);
},
});
}
get() {
console.log('get');
}
post() {
console.log('post');
}
delete() {}
}
Axios.create = Axios.prototype.create = function() {
let axios = new Axios();
axios.default = JSON.parse(JSON.stringify(_default));
return axios;
};
export default Axios.create();
这里是给原型和实例都加了一个create方法,因为咱们可能直接用axios.create()也可能直接用axios(),纯加静态方法或者实例方法都满足不了咱们的需求
这时候我们来实验一下,先来console一下axios.default
你会发现,undefined,这是为什么呢,在这里明明都已经添加了呀
因为这时候这个axios并不是一个对象,而是一个proxy,咱们还没给proxy加set方法对不对,加什么都加不上,这时候来改造一下代码
function request() {}
const _default = {
method: 'get',
baseUrl: "",
headers: {
common: {
'X-Request-By': 'XMLHttpRequest',
},
get: {},
post: {},
delete: {},
},
};
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
console.log(fn, thisArg, args);
},
});
}
get() {
console.log('get');
}
post() {
console.log('post');
}
delete() {}
}
Axios.create = Axios.prototype.create = function() {
let axios = new Axios();
axios.default = JSON.parse(JSON.stringify(_default));
return axios;
};
export default Axios.create();
这时候再看浏览器,就会发现这个default就有了
以及咱们来create两个axios,改一下参数试一下
两个实例参数也不影响,这也是很好的第n步,这时候咱们就已经完成axios的四分之一了
咱们现在实例间是不影响了,不过咱们改改参数的时候绝不止直接axios.default.xxx这么改,咱们还应该有参数,像这样
这里咱们可以直接改造一下axios.create方法
~ axios.js
...
Axios.create = Axios.prototype.create = function(options={}) {
let axios = new Axios();
axios.default = {
...JSON.parse(JSON.stringify(_default)),
...options
};
return axios;
};
...
直接展开替换一下就行了对不对,但是,真的么?
假设咱们直接传了一个对象,里面有个headers的话是不是就直接给咱们的默认参数header整个就得替换了呀,那这个就不太好,当然了,这个也看咱们对自己这个库的需求,如果咱们就想这么做,也到是没啥毛病,问题如下
那么这时候,咱们就可以用一个小小的递归来搞定了
~ axios.js
...
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
function merge(dest, src) {
for (let name in src) {
if (typeof src[name] == 'object') {
if (!dest[name]) {
dest[name] = {};
}
merge(dest[name], src[name]);
} else {
dest[name] = src[name];
}
}
}
merge(res, options);
axios.default = res;
return axios;
};
...
这时候再看,就没问题了
代码整理拆分
接下来,咱们先不急着去写请求的参数,咱们先把这个代码稍微规划规划,整理整理,毕竟全放在一个文件里,这个以后就没法维护了
目前拆分可以分几点
default是不是可以用一个单独的文件来装- 这个
merge函数肯定是公用的,可以放在咱们的common.js里 - 这个
request也应该单独放在一个js里来定义
废话不多说,直接上代码
~ request.js
export default function request() {
}
~ default.js
export default {
method: 'get',
baseUrl: '',
headers: {
common: {
'X-Request-By': 'XMLHttpRequest',
},
get: {},
post: {},
delete: {},
},
};
~ common.js
export function assert(exp, msg = 'assert faild') {
if (!exp) {
throw new Error(msg);
}
}
export function merge(dest, src) {
for (let name in src) {
if (typeof src[name] == 'object') {
if (!dest[name]) {
dest[name] = {};
}
merge(dest[name], src[name]);
} else {
dest[name] = src[name];
}
}
}
~ axios.js
import _default from './default';
import { merge } from './common';
import request from './request';
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
console.log(fn, thisArg, args);
},
});
}
get() {
console.log('get');
}
post() {
console.log('post');
}
delete() {}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
这时候就感觉干净不少了,对不对
处理请求参数
在写之前,咱们先来看看axios都有哪些支持的写法,然后再去考虑怎么写
大概来看 除了那个总的axios({...})这三种方法是不是差不多呀,当然了,axios里东西太多了,这里就简单实现这三个,说明问题为主,大家有兴趣可以自己加,无非是体力活
当然,可以看到axios参数情况还是蛮多的,这时候咱们应该直接统一处理,不管传过来什么参数,咱们都返回一个axios({})这种,最终统一处理,这不是方便么
这里直接来先判断一下前两种情况
你会发现前两种情况除了这个method都是一样的,那这个咱们就可以抽出方法统一处理
~ axios.js
import _default from './default';
import { merge } from './common';
import request from './request';
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
console.log(fn, thisArg, args);
},
});
}
_preprocessArgs(method, ...args) {
let options;
if (args.length == 1 && typeof args[0] == 'string') {
options = { method, url: args[0] };
} else if (args.length == 1 && args[0].constructor == Object) {
options = {
...args[0],
method,
};
} else {
return undefined;
}
return options;
}
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
}
}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
然后这时候,咱们以封装一个库的视角,肯定需要对参数进行各种各样的校验,类型等等,不对的话给他一个正经的报错,帮助使用者来调试
咱们之前在common.js里写的assert这时候就用上了
...
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
// ...
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
} else if (args.length == 3) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
} else {
assert(false, 'invaild argments');
}
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
}
}
}
...
这里的规则对应着上面写的axios使用方式,大体来说也差不多,把这些参数都校验好了,接下来,咱们就可以写具体的个性化的处理了
顺便一说,这个地方当然也是可以重用的,不过没必要,搞了一通其实也没减少多少代码,并且贼乱,也看个人,大家不喜欢可以自己修改
然后咱们再来处理一下这个options,并且console一下
~ axios.js
import _default from './default';
import { merge, assert } from './common';
import request from './request';
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
console.log(fn, thisArg, args);
},
});
}
_preprocessArgs(method, args) {
let options;
if (args.length == 1 && typeof args[0] == 'string') {
options = { method, url: args[0] };
} else if (args.length == 1 && args[0].constructor == Object) {
options = {
...args[0],
method,
};
} else {
return undefined;
}
console.log(options);
return options;
}
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
console.log(options);
} else {
assert(false, 'invaild args');
}
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
options = {
url: args[0],
data: args[1],
method: 'post',
};
console.log(options);
} else if (args.length == 3) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[2] == 'object' &&
args[2] &&
args[2].constructor == Object,
'args[2] must is JSON',
);
options = {
...args[2],
url: args[0],
data: args[1],
method: 'post',
};
console.log(options);
} else {
assert(false, 'invaild argments');
}
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
console.log(options);
}
}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
这时候咱们来测试一下
~ index.js
import Axios from './axios';
Axios.get('1.json');
Axios.get('1.json', { headers: { a: 12 } });
Axios.post('1.php');
Axios.post('1.php', { a: 12, b: 5 });
Axios.post('1.php', [12, 5, 6]);
let form = new FormData();
Axios.post('1.txt', form);
Axios.post('1.txt', 'dw1ewdq');
Axios.post('1.json', form, { headers: { a: 213, b: 132 } });
Axios.delete('1.json');
Axios.delete('1.json', { parmas: { id: 1 } });
这时候可以看到,妥妥的对不对?
然后呢。。。还没忘,咱们还需要处理直接apply的情况,也就是直接Axios()这么调用的时候
不废话,直接上代码,跟get其实差不多
~ axios.js
import _default from './default';
import { merge, assert } from './common';
import request from './request';
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
let options = _this._preprocessArgs(undefined, args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
};
console.log(options);
} else {
assert(false, 'invaild args');
}
}
},
});
}
_preprocessArgs(method, args) {
let options;
if (args.length == 1 && typeof args[0] == 'string') {
options = { method, url: args[0] };
} else if (args.length == 1 && args[0].constructor == Object) {
options = {
...args[0],
method,
};
} else {
return undefined;
}
console.log(options);
return options;
}
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
console.log(options);
} else {
assert(false, 'invaild args');
}
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
options = {
url: args[0],
data: args[1],
method: 'post',
};
console.log(options);
} else if (args.length == 3) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[2] == 'object' &&
args[2] &&
args[2].constructor == Object,
'args[2] must is JSON',
);
options = {
...args[2],
url: args[0],
data: args[1],
method: 'post',
};
console.log(options);
} else {
assert(false, 'invaild argments');
}
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
console.log(options);
}
}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
然后来测试一下
没问题,当然为什么method是undefined,因为这时候还没走到咱们的default呢,咱们是在这决定默认请求值的,所以这里直接给个undefined就行,然后咱们应该把些options全都和defaulft处理完,丢给咱们的request函数去请求
而这个方法肯定所有请求都需要,所以咱们写一个公共方法
这个request方法主要干的事四件事
- 跟this.default进行合并
- 检测参数是否正确
- baseUrl 合并请求
- 正式调用request(options)
import _default from './default';
import { merge, assert } from './common';
import request from './request';
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
let options = _this._preprocessArgs(undefined, args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
};
_this.request(options);
} else {
assert(false, 'invaild args');
}
}
},
});
}
_preprocessArgs(method, args) {
let options;
if (args.length == 1 && typeof args[0] == 'string') {
options = { method, url: args[0] };
this.request(options);
} else if (args.length == 1 && args[0].constructor == Object) {
options = {
...args[0],
method,
};
this.request(options);
} else {
return undefined;
}
return options;
}
request(options) {
console.log(options, 'request');
// 1. 跟this.default进行合并
// 2. 检测参数是否正确
// 3. baseUrl 合并请求
// 4. 正式调用request(options)
}
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
this.request(options);
} else {
assert(false, 'invaild args');
}
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
options = {
url: args[0],
data: args[1],
method: 'post',
};
this.request(options);
} else if (args.length == 3) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[2] == 'object' &&
args[2] &&
args[2].constructor == Object,
'args[2] must is JSON',
);
options = {
...args[2],
url: args[0],
data: args[1],
method: 'post',
};
this.request(options);
} else {
assert(false, 'invaild argments');
}
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
this.request(options);
}
}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
这个合并很简单,咱们之前写的merge函数又派上用场了,修改一下代码
...
request(options) {
console.log(options);
// 1. 跟this.default进行合并
merge(options, this.default);
console.log(options);
// 2. 检测参数是否正确
// 3. baseUrl 合并请求
// 4. 正式调用request(options)
}
...
这时候可以看到,合并前和合并后数据就已经都有了,但是,这时候咱们的header就不应该是全部都有了,应该根据传过来的method是什么来把对应的方式的header和common合并一下
request(options) {
// 1. 跟this.default进行合并
let _headers = this.default.headers;
delete this.default.headers;
merge(options, this.default);
this.default.headers = _headers;
//合并头
// this.default.headers.common -> this.default.headers.get -> options
let headers = {};
merge(headers, this.default.headers.common);
merge(headers, this.default.headers[options.method.toLowerCase()]);
merge(headers, options.headers);
console.log(headers);
console.log(options);
// 2. 检测参数是否正确
// 3. baseUrl 合并请求
// 4. 正式调用request(options)
}
这里比较乱,所以咱们先来捋一捋
咱们目的是要让header合并,不过合并的话就会有点小问题,之前在们在default的里定义的common、get...也都会被复制过来,如果要是咱们用if判断
options.header.common == this.default.headers.common然后delete的话,这时候你会发现不行,因为咱们也知道,你直接写两个对象判断就相当于直接new了两个对象,那这时候判断肯定不相等,那么咱们是在什么时候复制的呢
就在咱们封装的merge里,以及还有很多地方都动过这个东西
然后,咱们应该找到这个东西到底在什么时候就不一样了,其实也就是咱们这个request函数里第一次merge的时候
所以咱们这里玩了一个小技巧,因为common这些东西在底下都已经手动搞了,所以这里不需要他复制进来,所以先delete了一下
让他在之前就进不去,delete之后再给拿回去,两头不耽误,真好~
最后,咱们把headers赋值到咱们的options.headers
request(options) {
// 1. 合并头
let _headers = this.default.headers;
delete this.default.headers;
merge(options, this.default);
this.default.headers = _headers;
// this.default.headers.common -> this.default.headers.get -> options
let headers = {};
merge(headers, this.default.headers.common);
merge(headers, this.default.headers[options.method.toLowerCase()]);
merge(headers, options.headers);
options.headers = headers;
console.log(options);
// 3. baseUrl 合并请求
// 4. 正式调用request(options)
}
~ index.js
import Axios from './axios';
Axios('1.php');
Axios({
url: '2.php',
params: { a: 12, b: 3 },
headers: {
a: 12,
},
});
测试一下结果
可以看到,没毛病~
然后咱们再来看一下第二步,其实这个校验咱们可以写的非常非常多值得校验的事儿,但是这里只说明意思,写几个就算,大家有兴趣可以多补一些
...
assert(options.method, 'no method');
assert(typeof options.method == 'string', 'method must be string');
assert(options.url, 'no url');
assert(typeof options.url == 'string', 'url must be string');
...
第三步也是直接上代码了
~ axios.js
options.url=options.baseUrl+options.url;
delete options.baseUrl;
~ common.js
export function assert(exp, msg = 'assert faild') {
if (!exp) {
throw new Error(msg);
}
}
export function merge(dest, src) {
for (let name in src) {
if (typeof src[name] == 'object') {
if (!dest[name]) {
dest[name] = {};
}
merge(dest[name], src[name]);
} else {
if (dest[name] === undefined) {
dest[name] = src[name];
}
}
}
}
~ index.js
import Axios from './axios';
Axios('1.php', {
baseUrl: 'http://www.baidu.com/',
headers: {
a: 12,
},
});
Ï
这时候再测试一下,可以看到,就没问题了
这里说明一下为什么要改一下
merge,加了一个判断,因为咱们之前是直接替换掉,有没有都替换,这肯定不行,不加上的话会把咱们的baseUrl干了
当然了,还有个小事儿咱们需要处理一下,如果用你这个库的人脑子有病(当然,不考虑脑子有病的也可以),他写路径的时候是这么写的
你这个是不是又不行了呀,很简单,NodeJS里有一个url包,可以直接引过来用,webpack会帮你把他打包起来,不过有一点需要注意,webpack不是所有的东西都能打包的,比如fs模块,甚至一些底层功能用c和c++写的系统包这肯定就不行,不过一个url问题不大
~ axios.js
import _default from './default';
import { merge, assert } from './common';
import request from './request';
const urlLib = require('url');
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
let options = _this._preprocessArgs(undefined, args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
};
_this.request(options);
} else {
assert(false, 'invaild args');
}
}
},
});
}
_preprocessArgs(method, args) {
let options;
if (args.length == 1 && typeof args[0] == 'string') {
options = { method, url: args[0] };
this.request(options);
} else if (args.length == 1 && args[0].constructor == Object) {
options = {
...args[0],
method,
};
this.request(options);
} else {
return undefined;
}
return options;
}
request(options) {
// 1. 合并头
let _headers = this.default.headers;
delete this.default.headers;
merge(options, this.default);
this.default.headers = _headers;
// this.default.headers.common -> this.default.headers.get -> options
let headers = {};
merge(headers, this.default.headers.common);
merge(headers, this.default.headers[options.method.toLowerCase()]);
merge(headers, options.headers);
options.headers = headers;
console.log(options);
// 2. 检测参数是否正确
assert(options.method, 'no method');
assert(typeof options.method == 'string', 'method must be string');
assert(options.url, 'no url');
assert(typeof options.url == 'string', 'url must be string');
// 3. baseUrl 合并请求
options.url = urlLib.resolve(options.baseUrl, options.url);
delete options.baseUrl;
// 4. 正式调用request(options)
request(options);
}
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
this.request(options);
} else {
assert(false, 'invaild args');
}
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
options = {
url: args[0],
data: args[1],
method: 'post',
};
this.request(options);
} else if (args.length == 3) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[2] == 'object' &&
args[2] &&
args[2].constructor == Object,
'args[2] must is JSON',
);
options = {
...args[2],
url: args[0],
data: args[1],
method: 'post',
};
this.request(options);
} else {
assert(false, 'invaild argments');
}
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
this.request(options);
}
}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
这时候再测试一下
就没问题了
merge 问题
其实咱们这个merge现在还有很大的问题,因为咱们最开始的想要的功能和咱们现在的功能有差入
咱们现在是被迫写了一个if,改成了查漏补缺,那么其实后面有优先级的东西顺序应该都反过来
咱们最开始的需求是什么,是想让这个dest优先级最低,是可以被别人覆盖的,但是现在写了这个if之后,变成他优先级最高了,那这个就不对,但是还不能去掉,去掉了之后这个合并就又出问题了
其实应该怎么做,应该把这两个东西顺序颠倒一下,但是这时候又不对了,因为就导致这个
this.default变了,这时候又需要复制一份,咱们来写一个公共的方法放到common.js里
~ common.js
...
export function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}
以及咱们这个顺序稍微颠倒一下,然后数据克隆一下
这个时候来测试一下
会发现是不是header里的这些东西又回来了呀,原因也很简单,因为咱们在上面整个的default给clone下来了,所以咱们把delete这一块往上提提
这时候就没问题了
request
这个时候咱们就应该来写第四步了
直接把options给到咱们的request函数里就可以,零零碎碎的问题全都给它处理好了
然后来修改一下request.js
~ request.js
export default function request(options) {
console.log(options);
let xhr = new XMLHttpRequest();
xhr.open(options.method, options.url, true);
for (let name in options.headers) {
xhr.setRequestHeader(name, options.headers[name]);
}
xhr.send(options.data);
}
暂时先写一个简单的请求,然后咱们来测试一下看看能不能发出去
首先先建一个txt,方便咱们测试,我就放到data目录里了,像这样
然后改一下index.js
~ index.js
import Axios from './axios';
Axios('/data/1.txt', {
headers: {
a: 12,
},
});
这时候咱们可以看到,header是加上了的,并且返回的东西也对
当然这个东西还没完,也是一个防止用户捣乱的事
如果用户给你的header是这样的东西,那这个就不太好,所以最好还是给它来一个编码
~ request.js
export default function request(options) {
console.log(options);
let xhr = new XMLHttpRequest();
xhr.open(options.method, options.url, true);
for (let name in options.headers) {
xhr.setRequestHeader(
encodeURIComponent(name),
encodeURIComponent(options.headers[name]),
);
}
xhr.send(options.data);
}
这就没问题了万一后台有点问题,一碰见冒号就算结束这种问题就能避免了
然后咱们用的时候肯定更多的时候是需要一个async和await一下,所以咱们需要用Promise来包一下
~ axios.js
export default function request(options) {
console.log(options);
let xhr = new XMLHttpRequest();
xhr.open(options.method, options.url, true);
for (let name in options.headers) {
xhr.setRequestHeader(
encodeURIComponent(name),
encodeURIComponent(options.headers[name]),
);
}
xhr.send(options.data);
return new Promise((resolve, reject) => {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr);
} else {
reject(xhr);
}
}
};
});
}
以及这个时候,咱们还有很多很多问题
- 304其实也算成功,那这么封装也是就不对,而且用户可能有一些自定义的
code,这个怎么做到配置 - 咱们目前的
webpack只能兼容es6,async和await是兼容不了的,这个该怎么配
咱们先来解决webpack的问题,这个其实很简单,咱们需要再按一个包
yarn add @babel/polyfill
然后打开webpack.config.js修改一下entry
~ webpack.config.js
...
entry: ['@babel/polyfill','./src/index.js'],
...
注意这个顺序是不能颠倒的
这样就可以兼容了,然后咱们再来修改一下index.js
import Axios from './axios';
(async () => {
let res = await Axios('/data/1.txt', {
headers: {
a: 12,
b: '321fho:fdsf vfds; : ',
},
});
console.log(res);
})();
可以看到结果是undefined,这是因为咱们根本没有返回咱们的处理结果
这时候修改一下axios.js
import _default from './default';
import { merge, assert, clone } from './common';
import request from './request';
const urlLib = require('url');
class Axios {
constructor() {
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
let options = _this._preprocessArgs(undefined, args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
};
return _this.request(options);
} else {
assert(false, 'invaild args');
}
}
},
});
}
_preprocessArgs(method, args) {
let options;
if (args.length == 1 && typeof args[0] == 'string') {
options = { method, url: args[0] };
this.request(options);
} else if (args.length == 1 && args[0].constructor == Object) {
options = {
...args[0],
method,
};
this.request(options);
} else {
return undefined;
}
return options;
}
request(options) {
// 1. 合并头
let _headers = this.default.headers;
delete this.default.headers;
let result = clone(this.default);
merge(result, this.default);
merge(result, options);
this.default.headers = _headers;
options = result;
// this.default.headers.common -> this.default.headers.get -> options
let headers = {};
merge(headers, this.default.headers.common);
merge(headers, this.default.headers[options.method.toLowerCase()]);
merge(headers, options.headers);
options.headers = headers;
// 2. 检测参数是否正确
assert(options.method, 'no method');
assert(typeof options.method == 'string', 'method must be string');
assert(options.url, 'no url');
assert(typeof options.url == 'string', 'url must be string');
// 3. baseUrl 合并请求
options.url = urlLib.resolve(options.baseUrl, options.url);
delete options.baseUrl;
// 4. 正式调用request(options)
return request(options);
}
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
return this.request(options);
} else {
assert(false, 'invaild args');
}
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
options = {
url: args[0],
data: args[1],
method: 'post',
};
return this.request(options);
} else if (args.length == 3) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[2] == 'object' &&
args[2] &&
args[2].constructor == Object,
'args[2] must is JSON',
);
options = {
...args[2],
url: args[0],
data: args[1],
method: 'post',
};
return this.request(options);
} else {
assert(false, 'invaild argments');
}
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
return this.request(options);
}
}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = { ...JSON.parse(JSON.stringify(_default)) };
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
这时候咱们再看结果,是不是就可以了,当然了,咱们肯定不能就直接把原始的xml对象返回回去,咱们还要对返回的数据进行各种处理
处理数据
咱们先来简单的改一下axios.js 下的request
返回又一个promise,这时候可以看到结果,一点毛病没有
但是咱们这个东西全放在axios.js里就太乱了,所以咱们也单独把它拆除去
建两个文件一个是/src/response.js一个是/src/error.js
然后这里axios.js引入一下,并且处理的时候分别交给他们
然后response.js里直接返回值就可以了
不过这个headers稍微有点特别,需要单独调一个方法xhr.getAllResponseHeaders(),不过这返回的是原始xhr的头,这肯定不行,所以咱们需要来切一下
~ response.js
export default function(xhr) {
let arr = xhr.getAllResponseHeaders().split('\r\n');
let headers = {};
arr.forEach(str => {
if (!str) return;
let [name, val] = str.split(': ');
headers[name] = val;
});
return {
ok: true,
status: xhr.status,
statusText: xhr.statusText,
data: xhr.response,
headers,
xhr,
};
}
这样就行了
transformRequest && transformResponse
这时候当然还不算完,因为咱们现在的data还没有任何处理,所以肯定都是字符串,以及用户可能自定义这个处理方式,熟悉axios的朋友应该知道,axios有transformRequest和transformResponse方法
这时候咱们先来改一下之前axios.js里的request方法
现在需要在第三步和第四步之间来处理一下请求
先把参数打印一下,然后修改一下index.js的测试demo
为了测试方便我把1.txt改成1.json了,方便咱们一会处理json数据好看到效果
可以看到,这个参数是可以拿到的,那么接下来就比较简单了,直接来
这时候看请求,这个headers就加上了
这里稍微提一嘴,为什么我要delete掉,虽然不delete没什么关系,但是我希望我这个request保持干净
至于这个自定义返回结果,是不是就更简单了呀
这时候可以看眼结果,我没传transformResponse,结果是这样
这时候就可以了
当然了,咱们现在已经可以很灵活的运用了,不止单传这个json里可以配参数,全局配置统一处理一样是可以的,咱们来试试
以及不同的实例之间,都是可以的
拦截器
拦截器在一个请求库里肯定是必不可少的,其实咱们这个库写到现在,想加一个这玩意,其实是很容易的
~ index.js
import Axios from './axios';
Axios.interceptors.request.use(function(config) {
config.headers.interceptors = 'true';
return config;
});
(async () => {
let res = await Axios('/data/1.json', {
headers: {
a: 12,
b: '321fho:fdsf vfds; : ',
},
});
console.log(res);
})();
然后新建一个interceptor.js
~interceptor.js
class Interceptor {
constructor() {
this._list = [];
}
use(fn) {
this._list.push(fn);
}
list() {
return this._list;
}
}
export default Interceptor;
~ axios.js
import _default from './default';
import { merge, assert, clone } from './common';
import request from './request';
import createResponse from './response';
import createError from './error';
const urlLib = require('url');
import Interceptor from './interceptor';
class Axios {
constructor() {
this.interceptors = {
request: new Interceptor(),
response: new Interceptor(),
};
let _this = this;
return new Proxy(request, {
get(data, name) {
return _this[name];
},
set(data, name, val) {
_this[name] = val;
return true;
},
apply(fn, thisArg, args) {
let options = _this._preprocessArgs(undefined, args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
};
return _this.request(options);
} else {
assert(false, 'invaild args');
}
}
},
});
}
_preprocessArgs(method, args) {
let options;
if (args.length == 1 && typeof args[0] == 'string') {
options = { method, url: args[0] };
this.request(options);
} else if (args.length == 1 && args[0].constructor == Object) {
options = {
...args[0],
method,
};
this.request(options);
} else {
return undefined;
}
return options;
}
request(options) {
// 1. 合并头
let _headers = this.default.headers;
delete this.default.headers;
let result = clone(this.default);
merge(result, this.default);
merge(result, options);
this.default.headers = _headers;
options = result;
// this.default.headers.common -> this.default.headers.get -> options
let headers = {};
merge(headers, this.default.headers.common);
merge(headers, this.default.headers[options.method.toLowerCase()]);
merge(headers, options.headers);
options.headers = headers;
// 2. 检测参数是否正确
assert(options.method, 'no method');
assert(typeof options.method == 'string', 'method must be string');
assert(options.url, 'no url');
assert(typeof options.url == 'string', 'url must be string');
// 3. baseUrl 合并请求
options.url = urlLib.resolve(options.baseUrl, options.url);
delete options.baseUrl;
// 4. 变换一下请求
const { transformRequest, transformResponse } = options;
delete options.transformRequest;
delete options.transformResponse;
if (transformRequest) options = transformRequest(options);
let list = this.interceptors.request.list();
list.forEach(fn => {
options = fn(options);
});
// 5. 正式调用request(options)
return new Promise((resolve, reject) => {
return request(options).then(
xhr => {
let res = createResponse(xhr);
if (transformResponse) res = transformResponse(res);
let list = this.interceptors.response.list();
list.forEach(fn => {
res = fn(res);
});
resolve(res);
},
xhr => {
let err = createError(xhr);
reject(err);
},
);
});
}
get(...args) {
let options = this._preprocessArgs('get', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' &&
args[1] &&
args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
return this.request(options);
} else {
assert(false, 'invaild args');
}
}
}
post(...args) {
let options = this._preprocessArgs('post', args);
if (!options) {
if (args.length == 2) {
assert(typeof args[0] == 'string', 'args[0] must is string');
options = {
url: args[0],
data: args[1],
method: 'post',
};
return this.request(options);
} else if (args.length == 3) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[2] == 'object' &&
args[2] &&
args[2].constructor == Object,
'args[2] must is JSON',
);
options = {
...args[2],
url: args[0],
data: args[1],
method: 'post',
};
return this.request(options);
} else {
assert(false, 'invaild argments');
}
}
}
delete(...args) {
let options = this._preprocessArgs('delete', args);
if (!options) {
assert(typeof args[0] == 'string', 'args[0] must is string');
assert(
typeof args[1] == 'object' && args[1] && args[1].constructor == Object,
'args[1] must is JSON',
);
options = {
...args[1],
url: args[0],
method: 'get',
};
return this.request(options);
}
}
}
Axios.create = Axios.prototype.create = function(options = {}) {
let axios = new Axios();
let res = clone(_default);
merge(res, options);
axios.default = res;
return axios;
};
export default Axios.create();
可以看到,基本上是一样的套路,主要就是把参数传过来然后调用一下而已
不过这里面有两个小小的问题需要处理
- 咱们现在给用户开了很多口,如果他要是返回的
config不对或者没返回咱们理应给他返回错误信息,再校验一下,这时候大家应该能想到了,应该吧axios校验这些参数单独搞一个函数,没什么技术含量,这里就不多赘述,大家有兴趣可以试一下 - 咱们给用户的是函数,用的是
forEach,这时候就会导致一个问题,如果用户给你的是一个带async的函数那就不行了,咱们也要加上async和await,不过async和await里面返回一个promise又很怪,这个大家有兴趣可以自己试试,或者评论区留言
这时候来试一下效果
可以看到咱们这个拦截器也算是完成了
总结
本篇最终代码已上传github,链接如下
github.com/Mikey-9/axi…
还是那句话,咱们本篇文章主要不是要完整的实现axios,而是实现一个这样的库的思想,当然其中也有很多问题,欢迎大家在评论区留言或者可以加我的qq或者微信一起交流
篇幅略长,感谢观看