diff --git a/.eslintrc.json b/.eslintrc.json index 01ff112..b9df4d6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -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 } } diff --git a/docs/pages/advanced/adapter.md b/docs/pages/advanced/adapter.md index 1a09165..d0a7aff 100644 --- a/docs/pages/advanced/adapter.md +++ b/docs/pages/advanced/adapter.md @@ -259,7 +259,7 @@ axios.defaults.adapter = axios.createAdapter({ }); ``` -可以使用 `createAdapter` 彻底抹平存在差异的部分,实现全平台完美适配。 +可以进一步抹平存在差异的部分,实现完美适配全平台。 ```ts import axios from 'axios-miniprogram'; diff --git a/src/adapter.ts b/src/adapter.ts index 65967a5..3c76fd5 100644 --- a/src/adapter.ts +++ b/src/adapter.ts @@ -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) && diff --git a/src/axios.ts b/src/axios.ts index b53fb28..bc3b47e 100644 --- a/src/axios.ts +++ b/src/axios.ts @@ -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; } diff --git a/src/core/Axios.ts b/src/core/Axios.ts index 5c5c81e..7b2f754 100644 --- a/src/core/Axios.ts +++ b/src/core/Axios.ts @@ -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 | Interceptor, + ] = [ + { + 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; } } diff --git a/src/core/AxiosDomain.ts b/src/core/AxiosDomain.ts index 752d7e1..611282d 100644 --- a/src/core/AxiosDomain.ts +++ b/src/core/AxiosDomain.ts @@ -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 = {}) { - config.method = alias; - return this.request(url, config); - }; - } - } - - #createAspRequests() { - for (const alias of AxiosDomain.asp) { - this[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 = {}) { - config.method = alias; - config.data = deepMerge(data, config.data ?? {}); - return this.request(url, config); - }; - } } } + +for (const alias of AxiosDomain.as) { + AxiosDomain.prototype[alias] = function processAsRequest(url, config = {}) { + config.method = alias; + return this.request(url, 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); + }; +} + +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); + }; +} diff --git a/src/core/InterceptorManager.ts b/src/core/InterceptorManager.ts index 56c9785..271eca1 100644 --- a/src/core/InterceptorManager.ts +++ b/src/core/InterceptorManager.ts @@ -18,27 +18,33 @@ export interface InterceptorExecutor { export default class InterceptorManager { #id = 0; - #interceptors: AnyObject> = {}; + #interceptors = new Map>(); + + get size() { + return this.#interceptors.size; + } use( resolved: InterceptorResolved, rejected?: InterceptorRejected, ): 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, reverse?: boolean): void { - let interceptors: Interceptor[] = Object.values(this.#interceptors); - if (reverse) interceptors = interceptors.reverse(); - interceptors.forEach(executor); + clear() { + this.#interceptors.clear(); + } + + forEach(executor: InterceptorExecutor): void { + this.#interceptors.forEach(executor); } } diff --git a/src/core/mergeConfig.ts b/src/core/mergeConfig.ts index c5f447c..fb73aff 100644 --- a/src/core/mergeConfig.ts +++ b/src/core/mergeConfig.ts @@ -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]) { diff --git a/src/helpers/isTypes.ts b/src/helpers/isTypes.ts index e49d6d1..147052a 100644 --- a/src/helpers/isTypes.ts +++ b/src/helpers/isTypes.ts @@ -14,7 +14,7 @@ export function isString(value: any): value is string { ); } -export function isPlainObject(value: any): value is object & AnyObject { +export function isPlainObject(value: any): value is T { return _toString.call(value) === '[object Object]'; } diff --git a/test/core/InterceptorManager.test.ts b/test/core/InterceptorManager.test.ts index bb9fb90..890dee7 100644 --- a/test/core/InterceptorManager.test.ts +++ b/test/core/InterceptorManager.test.ts @@ -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, - }); - }); });