|
| 1 | +import FormData from 'form-data'; |
| 2 | + |
| 3 | +class FetchAxios { |
| 4 | + constructor(defaultConfig = {}) { |
| 5 | + this.defaults = { |
| 6 | + baseURL: '', |
| 7 | + headers: {}, |
| 8 | + timeout: 0, |
| 9 | + responseType: 'json', // json 或 text |
| 10 | + withCredentials: false, |
| 11 | + httpsAgent: null, |
| 12 | + ...defaultConfig, |
| 13 | + }; |
| 14 | + this.interceptors = {request: [], response: []}; |
| 15 | + } |
| 16 | + |
| 17 | + useRequestInterceptor(fn) { |
| 18 | + this.interceptors.request.push(fn); |
| 19 | + } |
| 20 | + |
| 21 | + useResponseInterceptor(fn) { |
| 22 | + this.interceptors.response.push(fn); |
| 23 | + } |
| 24 | + |
| 25 | + async request(urlOrConfig, config = {}) { |
| 26 | + let finalConfig = {}; |
| 27 | + // 判断调用方式 |
| 28 | + if (typeof urlOrConfig === 'string') { |
| 29 | + finalConfig = {...this.defaults, ...config, url: this.defaults.baseURL + urlOrConfig}; |
| 30 | + } else { |
| 31 | + finalConfig = {...this.defaults, ...urlOrConfig, url: this.defaults.baseURL + (urlOrConfig.url || '')}; |
| 32 | + } |
| 33 | + |
| 34 | + // 执行请求拦截器 |
| 35 | + for (const interceptor of this.interceptors.request) { |
| 36 | + finalConfig = await interceptor(finalConfig) || finalConfig; |
| 37 | + } |
| 38 | + |
| 39 | + // 拼接 params |
| 40 | + if (finalConfig.params) { |
| 41 | + const query = new URLSearchParams(finalConfig.params).toString(); |
| 42 | + finalConfig.url += (finalConfig.url.includes('?') ? '&' : '?') + query; |
| 43 | + } |
| 44 | + |
| 45 | + const controller = new AbortController(); |
| 46 | + if (finalConfig.timeout) setTimeout(() => controller.abort(), finalConfig.timeout); |
| 47 | + |
| 48 | + const fetchOptions = { |
| 49 | + method: (finalConfig.method || 'GET').toUpperCase(), |
| 50 | + headers: {...finalConfig.headers}, |
| 51 | + signal: controller.signal, |
| 52 | + credentials: finalConfig.withCredentials ? 'include' : 'same-origin', |
| 53 | + agent: finalConfig.httpsAgent || undefined, |
| 54 | + }; |
| 55 | + |
| 56 | + if (finalConfig.data instanceof FormData) { |
| 57 | + fetchOptions.body = finalConfig.data; |
| 58 | + Object.assign(fetchOptions.headers, finalConfig.data.getHeaders()); |
| 59 | + } else if (finalConfig.data) { |
| 60 | + if (typeof finalConfig.data === 'object' && !fetchOptions.headers['Content-Type']) { |
| 61 | + fetchOptions.headers['Content-Type'] = 'application/json'; |
| 62 | + } |
| 63 | + fetchOptions.body = fetchOptions.headers['Content-Type'] === 'application/json' |
| 64 | + ? JSON.stringify(finalConfig.data) |
| 65 | + : finalConfig.data; |
| 66 | + } |
| 67 | + |
| 68 | + try { |
| 69 | + const response = await fetch(finalConfig.url, fetchOptions); |
| 70 | + let responseData; |
| 71 | + if (finalConfig.responseType === 'json') { |
| 72 | + responseData = await response.json().catch(() => null); |
| 73 | + } else { |
| 74 | + responseData = await response.text(); |
| 75 | + } |
| 76 | + |
| 77 | + let result = { |
| 78 | + data: responseData, |
| 79 | + status: response.status, |
| 80 | + statusText: response.statusText, |
| 81 | + headers: Object.fromEntries(response.headers.entries()), |
| 82 | + config: finalConfig, |
| 83 | + request: finalConfig.url, |
| 84 | + }; |
| 85 | + |
| 86 | + for (const interceptor of this.interceptors.response) { |
| 87 | + result = await interceptor(result) || result; |
| 88 | + } |
| 89 | + |
| 90 | + if (!response.ok) throw result; |
| 91 | + return result; |
| 92 | + } catch (err) { |
| 93 | + throw err; |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + get(url, config) { |
| 98 | + return this.request(url, {...config, method: 'GET'}); |
| 99 | + } |
| 100 | + |
| 101 | + post(url, data, config) { |
| 102 | + return this.request(url, {...config, method: 'POST', data}); |
| 103 | + } |
| 104 | + |
| 105 | + put(url, data, config) { |
| 106 | + return this.request(url, {...config, method: 'PUT', data}); |
| 107 | + } |
| 108 | + |
| 109 | + delete(url, config) { |
| 110 | + return this.request(url, {...config, method: 'DELETE'}); |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +// 创建 axios 实例函数 |
| 115 | +export function createInstance(defaultConfig) { |
| 116 | + const context = new FetchAxios(defaultConfig); |
| 117 | + |
| 118 | + // 创建可调用函数 |
| 119 | + const instance = context.request.bind(context); |
| 120 | + |
| 121 | + // 挂载方法 |
| 122 | + ['get', 'post', 'put', 'delete', 'useRequestInterceptor', 'useResponseInterceptor'].forEach(method => { |
| 123 | + instance[method] = context[method].bind(context); |
| 124 | + }); |
| 125 | + |
| 126 | + return instance; |
| 127 | +} |
0 commit comments