feat: 支持清空拦截器
parent
09ac2a94e2
commit
cbcc43ad77
|
@ -9,6 +9,7 @@
|
|||
"root": true,
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,7 +259,7 @@ axios.defaults.adapter = axios.createAdapter({
|
|||
});
|
||||
```
|
||||
|
||||
可以使用 `createAdapter` 彻底抹平存在差异的部分,实现全平台完美适配。
|
||||
可以进一步抹平存在差异的部分,实现完美适配全平台。
|
||||
|
||||
```ts
|
||||
import axios from 'axios-miniprogram';
|
||||
|
|
|
@ -337,7 +337,7 @@ export function createAdapter(platform: AxiosPlatform) {
|
|||
return adapter;
|
||||
}
|
||||
|
||||
export function isPlatform(value: unknown): value is AxiosPlatform {
|
||||
export function isPlatform(value: any): value is AxiosPlatform {
|
||||
return (
|
||||
isPlainObject(value) &&
|
||||
isFunction(value.request) &&
|
||||
|
|
|
@ -57,13 +57,8 @@ function createInstance(defaults: AxiosRequestConfig) {
|
|||
const context = new Axios(defaults);
|
||||
const instance = context.request as AxiosInstance;
|
||||
|
||||
Object.assign(instance, context, {
|
||||
// instance.fork 内部调用了 context 的私有方法
|
||||
// 所以直接调用 instance.fork 会导致程序抛出无法访问 context 私有方法的异常
|
||||
// instance.fork 调用时 this 重新指向 context,解决此问题
|
||||
fork: context.fork.bind(context),
|
||||
});
|
||||
Object.setPrototypeOf(instance, Object.getPrototypeOf(context));
|
||||
Object.assign(instance, context);
|
||||
Object.setPrototypeOf(instance, Axios.prototype);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,13 @@ import {
|
|||
AxiosAdapterResponseError,
|
||||
AxiosAdapterResponseData,
|
||||
} from '../adapter';
|
||||
import InterceptorManager, { Interceptor } from './InterceptorManager';
|
||||
import { mergeConfig } from './mergeConfig';
|
||||
import { CancelToken } from './cancel';
|
||||
import { dispatchRequest } from './dispatchRequest';
|
||||
import { AxiosTransformer } from './transformData';
|
||||
|
||||
import AxiosDomain from './AxiosDomain';
|
||||
import InterceptorManager from './InterceptorManager';
|
||||
|
||||
export type AxiosRequestMethod =
|
||||
| AxiosAdapterRequestMethod
|
||||
|
@ -243,26 +244,36 @@ export default class Axios extends AxiosDomain {
|
|||
/**
|
||||
* 派生领域
|
||||
*/
|
||||
fork(defaults: AxiosRequestConfig = {}) {
|
||||
fork = (defaults: AxiosRequestConfig = {}) => {
|
||||
if (isString(defaults.baseURL) && !isAbsoluteURL(defaults.baseURL)) {
|
||||
defaults.baseURL = combineURL(this.defaults.baseURL, defaults.baseURL);
|
||||
}
|
||||
return new AxiosDomain(mergeConfig(this.defaults, defaults), (config) =>
|
||||
this.#processRequest(config),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#processRequest(config: AxiosRequestConfig) {
|
||||
const { request, response } = this.interceptors;
|
||||
const chain: [
|
||||
Interceptor<AxiosRequestConfig> | Interceptor<AxiosResponse>,
|
||||
] = [
|
||||
{
|
||||
resolved: dispatchRequest,
|
||||
},
|
||||
];
|
||||
|
||||
let promiseRequest = Promise.resolve(config);
|
||||
request.forEach(({ resolved, rejected }) => {
|
||||
promiseRequest = promiseRequest.then(resolved, rejected);
|
||||
}, true);
|
||||
let promiseResponse = promiseRequest.then(dispatchRequest);
|
||||
response.forEach(({ resolved, rejected }) => {
|
||||
promiseResponse = promiseResponse.then(resolved, rejected);
|
||||
});
|
||||
return promiseResponse;
|
||||
this.interceptors.request.forEach(chain.unshift.bind(chain));
|
||||
this.interceptors.response.forEach(chain.push.bind(chain));
|
||||
|
||||
let next = Promise.resolve(config);
|
||||
for (const { resolved, rejected } of chain) {
|
||||
next = next.then(
|
||||
// @ts-ignore
|
||||
resolved,
|
||||
rejected,
|
||||
);
|
||||
}
|
||||
|
||||
return next as Promise<AxiosResponse>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,38 +167,36 @@ export default class AxiosDomain {
|
|||
|
||||
return processRequest(mergeConfig(this.defaults, config));
|
||||
};
|
||||
|
||||
this.#createAsRequests();
|
||||
this.#createAspRequests();
|
||||
this.#createAsdRequests();
|
||||
}
|
||||
}
|
||||
|
||||
#createAsRequests() {
|
||||
for (const alias of AxiosDomain.as) {
|
||||
this[alias] = function processAsRequest(url, config = {}) {
|
||||
for (const alias of AxiosDomain.as) {
|
||||
AxiosDomain.prototype[alias] = function processAsRequest(url, config = {}) {
|
||||
config.method = alias;
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#createAspRequests() {
|
||||
for (const alias of AxiosDomain.asp) {
|
||||
this[alias] = function processAspRequest(url, params = {}, config = {}) {
|
||||
for (const alias of AxiosDomain.asp) {
|
||||
AxiosDomain.prototype[alias] = function processAspRequest(
|
||||
url,
|
||||
params = {},
|
||||
config = {},
|
||||
) {
|
||||
config.method = alias;
|
||||
config.params = deepMerge(params, config.params ?? {});
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#createAsdRequests() {
|
||||
for (const alias of AxiosDomain.asd) {
|
||||
this[alias] = function processAsdRequest(url, data = {}, config = {}) {
|
||||
for (const alias of AxiosDomain.asd) {
|
||||
AxiosDomain.prototype[alias] = function processAsdRequest(
|
||||
url,
|
||||
data = {},
|
||||
config = {},
|
||||
) {
|
||||
config.method = alias;
|
||||
config.data = deepMerge(data, config.data ?? {});
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,27 +18,33 @@ export interface InterceptorExecutor<T = unknown> {
|
|||
export default class InterceptorManager<T = unknown> {
|
||||
#id = 0;
|
||||
|
||||
#interceptors: AnyObject<Interceptor<T>> = {};
|
||||
#interceptors = new Map<number, Interceptor<T>>();
|
||||
|
||||
get size() {
|
||||
return this.#interceptors.size;
|
||||
}
|
||||
|
||||
use(
|
||||
resolved: InterceptorResolved<T>,
|
||||
rejected?: InterceptorRejected<T>,
|
||||
): number {
|
||||
this.#interceptors[++this.#id] = {
|
||||
this.#interceptors.set(++this.#id, {
|
||||
resolved,
|
||||
rejected,
|
||||
};
|
||||
});
|
||||
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
eject(id: number): void {
|
||||
delete this.#interceptors[id];
|
||||
eject(id: number): boolean {
|
||||
return this.#interceptors.delete(id);
|
||||
}
|
||||
|
||||
forEach(executor: InterceptorExecutor<T>, reverse?: boolean): void {
|
||||
let interceptors: Interceptor<T>[] = Object.values(this.#interceptors);
|
||||
if (reverse) interceptors = interceptors.reverse();
|
||||
interceptors.forEach(executor);
|
||||
clear() {
|
||||
this.#interceptors.clear();
|
||||
}
|
||||
|
||||
forEach(executor: InterceptorExecutor<T>): void {
|
||||
this.#interceptors.forEach(executor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ export function mergeConfig(
|
|||
);
|
||||
|
||||
for (const key of keysSet) {
|
||||
const val1 = config1[key] as any;
|
||||
const val2 = config2[key] as any;
|
||||
const val1 = config1[key];
|
||||
const val2 = config2[key];
|
||||
|
||||
// 只从 config2 中取值
|
||||
if (fromConfig2Map[key]) {
|
||||
|
|
|
@ -14,7 +14,7 @@ export function isString(value: any): value is string {
|
|||
);
|
||||
}
|
||||
|
||||
export function isPlainObject(value: any): value is object & AnyObject {
|
||||
export function isPlainObject<T extends AnyObject>(value: any): value is T {
|
||||
return _toString.call(value) === '[object Object]';
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,12 @@ describe('src/core/InterceptorManager.ts', () => {
|
|||
const rej = vi.fn();
|
||||
const cb = vi.fn();
|
||||
|
||||
expect(i.size).toBe(0);
|
||||
|
||||
const id = i.use(res, rej);
|
||||
|
||||
expect(i.size).toBe(1);
|
||||
|
||||
i.forEach(({ resolved, rejected }) => {
|
||||
expect(resolved).toBe(res);
|
||||
expect(rejected).toBe(rej);
|
||||
|
@ -26,10 +30,31 @@ describe('src/core/InterceptorManager.ts', () => {
|
|||
i.eject(id);
|
||||
i.forEach(cb);
|
||||
|
||||
expect(i.size).toBe(0);
|
||||
|
||||
expect(cb).not.toBeCalled();
|
||||
});
|
||||
|
||||
test('应该可以依次执行拦截处理函数', () => {
|
||||
test('应该可以清理所有拦截处理函数', () => {
|
||||
const i = new InterceptorManager();
|
||||
const res = vi.fn();
|
||||
const rej = vi.fn();
|
||||
const cb = vi.fn();
|
||||
|
||||
expect(i.size).toBe(0);
|
||||
|
||||
i.use(res, rej);
|
||||
i.use(res, rej);
|
||||
i.use(res, rej);
|
||||
|
||||
expect(i.size).toBe(3);
|
||||
|
||||
i.clear();
|
||||
|
||||
expect(i.size).toBe(0);
|
||||
});
|
||||
|
||||
test('应该可以调用 forEach', () => {
|
||||
const i = new InterceptorManager();
|
||||
const res1 = vi.fn();
|
||||
const rej1 = vi.fn();
|
||||
|
@ -50,26 +75,4 @@ describe('src/core/InterceptorManager.ts', () => {
|
|||
rejected: rej2,
|
||||
});
|
||||
});
|
||||
|
||||
test('应该可以反向依次执行拦截处理函数', () => {
|
||||
const i = new InterceptorManager();
|
||||
const res1 = vi.fn();
|
||||
const rej1 = vi.fn();
|
||||
const res2 = vi.fn();
|
||||
const rej2 = vi.fn();
|
||||
const cb = vi.fn();
|
||||
|
||||
i.use(res1, rej1);
|
||||
i.use(res2, rej2);
|
||||
i.forEach(cb, true);
|
||||
|
||||
expect(cb.mock.calls[0][0]).toEqual({
|
||||
resolved: res2,
|
||||
rejected: rej2,
|
||||
});
|
||||
expect(cb.mock.calls[1][0]).toEqual({
|
||||
resolved: res1,
|
||||
rejected: rej1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue