etherreq 1.1.16 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -47
- package/dist/cache.js +281 -0
- package/dist/etherreq.js +84 -0
- package/dist/index.js +59 -0
- package/dist/request.js +96 -0
- package/dist/types/cache.d.ts +122 -0
- package/dist/types/cache.js +2 -0
- package/dist/types/config.js +58 -0
- package/dist/types/etherreq.d.ts +20 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/request.d.ts +10 -0
- package/dist/types/types/cache.d.ts +95 -0
- package/dist/types/types/config.d.ts +59 -0
- package/package.json +29 -19
- package/.github/workfiows/publish-npm.yml +0 -30
- package/.github/workflows/npm-publish.yml +0 -33
- package/src/cache.js +0 -58
- package/src/etherreq.js +0 -95
- package/src/index.js +0 -79
- package/src/request.js +0 -88
- package/src/types/etherreq.d.ts +0 -50
- package/src/upload.js +0 -88
package/README.md
CHANGED
|
@@ -1,80 +1,206 @@
|
|
|
1
|
-
#
|
|
1
|
+
# EtherReq
|
|
2
2
|
|
|
3
|
-
一个轻量级、无感知的 HTTP 请求库,基于
|
|
3
|
+
一个轻量级、无感知的 HTTP 请求库,基于 Fetch 封装,支持自动 Token 注入、拦截器、TypeScript 类型定义和智能缓存功能。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 特性
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- 🚀 **轻量级**: 基于原生 Fetch API 构建
|
|
8
|
+
- 🔐 **自动认证**: login 方法自动管理 token
|
|
9
|
+
- 🔄 **智能缓存**: 支持内存和 localStorage 双重缓存
|
|
10
|
+
- 🎯 **TypeScript**: 完整的类型支持
|
|
11
|
+
- ⚡ **拦截器**: 请求/响应拦截器支持
|
|
12
|
+
- 📦 **零依赖**: 无需额外依赖包
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
10
17
|
npm install etherreq
|
|
11
18
|
# 或者
|
|
12
19
|
yarn add etherreq
|
|
13
20
|
```
|
|
14
21
|
|
|
15
|
-
##
|
|
22
|
+
## 快速开始
|
|
16
23
|
|
|
17
|
-
```
|
|
24
|
+
```typescript
|
|
18
25
|
import { etherreq } from 'etherreq';
|
|
19
26
|
|
|
20
|
-
// GET
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const a= await etherreq.get('http://localhost:8081/api/users')
|
|
24
|
-
//获取gat数据
|
|
25
|
-
console.log(a);
|
|
26
|
-
}
|
|
27
|
+
// GET 请求(自动缓存)
|
|
28
|
+
const users = await etherreq.get('http://localhost:8081/api/users');
|
|
29
|
+
console.log(users);
|
|
27
30
|
|
|
28
31
|
// POST 请求
|
|
29
|
-
etherreq.post('/login', {
|
|
30
|
-
|
|
32
|
+
const result = await etherreq.post('/login', {
|
|
33
|
+
username: 'test',
|
|
34
|
+
password: '123456'
|
|
31
35
|
});
|
|
36
|
+
console.log('登录成功:', result);
|
|
37
|
+
|
|
38
|
+
// 带数据的POST请求
|
|
39
|
+
const user = {
|
|
40
|
+
id: 1,
|
|
41
|
+
name: '张三',
|
|
42
|
+
sex: '男',
|
|
43
|
+
age: 18,
|
|
44
|
+
};
|
|
45
|
+
const newUser = await etherreq.post('/add', user);
|
|
46
|
+
console.log(newUser);
|
|
47
|
+
```
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
const user={
|
|
35
|
-
id : 1,
|
|
36
|
-
name : 'name',
|
|
37
|
-
sex : '男',
|
|
38
|
-
age : 18,
|
|
39
|
-
}
|
|
40
|
-
const b1 =await etherreq.post('/add',user);
|
|
41
|
-
console.log(b1);
|
|
42
|
-
};
|
|
49
|
+
## 登录认证
|
|
43
50
|
|
|
51
|
+
```typescript
|
|
44
52
|
// 登录方法(自动保存 token)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
const login =await etherreq.login('users/login',user);
|
|
52
|
-
console.log(login);
|
|
53
|
+
const login = async () => {
|
|
54
|
+
const userData = {
|
|
55
|
+
id: 1,
|
|
56
|
+
username: "zhy",
|
|
57
|
+
password: "123456"
|
|
53
58
|
};
|
|
59
|
+
|
|
60
|
+
const loginResult = await etherreq.login('users/login', userData);
|
|
61
|
+
console.log(loginResult);
|
|
62
|
+
// 后续请求会自动携带 token
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 缓存功能
|
|
67
|
+
|
|
68
|
+
### 自动缓存
|
|
69
|
+
GET 请求默认启用智能缓存:
|
|
70
|
+
- 内存缓存(快速访问)
|
|
71
|
+
- localStorage 持久化(页面刷新后仍有效)
|
|
72
|
+
- 自动过期清理(默认5分钟)
|
|
54
73
|
|
|
74
|
+
### 缓存控制选项
|
|
55
75
|
|
|
76
|
+
```typescript
|
|
77
|
+
// 禁用缓存
|
|
78
|
+
const data = await etherreq.get('/api/data', {
|
|
79
|
+
cache: false
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// 自定义缓存时间
|
|
83
|
+
const data = await etherreq.get('/api/data', {
|
|
84
|
+
cache: {
|
|
85
|
+
enabled: true,
|
|
86
|
+
ttl: 10 * 60 * 1000 // 10分钟
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// 指定存储策略
|
|
91
|
+
const data = await etherreq.get('/api/data', {
|
|
92
|
+
cache: {
|
|
93
|
+
storage: 'memory+local', // 'memory' | 'local' | 'memory+local'
|
|
94
|
+
ttl: 300000
|
|
95
|
+
}
|
|
96
|
+
});
|
|
56
97
|
```
|
|
57
98
|
|
|
99
|
+
### 带参数的请求缓存
|
|
100
|
+
```typescript
|
|
101
|
+
// 不同参数生成不同缓存键
|
|
102
|
+
const page1 = await etherreq.get('/users', {
|
|
103
|
+
params: { page: 1, limit: 10 }
|
|
104
|
+
});
|
|
58
105
|
|
|
106
|
+
const page2 = await etherreq.get('/users', {
|
|
107
|
+
params: { page: 2, limit: 10 }
|
|
108
|
+
});
|
|
109
|
+
// 这两个请求会有独立的缓存
|
|
110
|
+
```
|
|
59
111
|
|
|
112
|
+
## API 接口
|
|
60
113
|
|
|
114
|
+
### 请求方法
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
etherreq(url, options?) // 默认 GET
|
|
118
|
+
etherreq.get(url, options?)
|
|
119
|
+
etherreq.post(url, data?, options?)
|
|
120
|
+
etherreq.put(url, data?, options?)
|
|
121
|
+
etherreq.delete(url, options?)
|
|
122
|
+
etherreq.del(url, options?) // delete 别名
|
|
123
|
+
etherreq.head(url, options?)
|
|
124
|
+
etherreq.options(url, options?)
|
|
125
|
+
etherreq.patch(url, data?, options?)
|
|
126
|
+
etherreq.login(url, data?, options?)
|
|
127
|
+
```
|
|
61
128
|
|
|
62
|
-
|
|
129
|
+
### 配置选项
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
interface EtherRequestOptions {
|
|
133
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
134
|
+
headers?: Record<string, string>;
|
|
135
|
+
body?: any;
|
|
136
|
+
params?: Record<string, string | number | boolean>;
|
|
137
|
+
baseURL?: string;
|
|
138
|
+
cache?: CacheOptions | false;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
63
141
|
|
|
64
|
-
###
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
142
|
+
### 缓存选项
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface CacheOptions {
|
|
146
|
+
enabled?: boolean; // 是否启用缓存
|
|
147
|
+
storage?: 'memory' | 'local' | 'memory+local'; // 存储策略
|
|
148
|
+
ttl?: number; // 过期时间(毫秒)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 高级用法
|
|
69
153
|
|
|
70
|
-
|
|
71
|
-
|
|
154
|
+
### 设置基础URL
|
|
155
|
+
```typescript
|
|
156
|
+
import { setBaseURL } from 'etherreq';
|
|
72
157
|
|
|
73
|
-
|
|
74
|
-
|
|
158
|
+
setBaseURL('https://api.example.com');
|
|
159
|
+
// 后续请求将使用此基础URL
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 并发请求
|
|
163
|
+
```typescript
|
|
164
|
+
const [users, posts] = await Promise.all([
|
|
165
|
+
etherreq.get('/users'),
|
|
166
|
+
etherreq.get('/posts')
|
|
167
|
+
]);
|
|
168
|
+
```
|
|
75
169
|
|
|
76
|
-
|
|
77
|
-
|
|
170
|
+
### 错误处理
|
|
171
|
+
```typescript
|
|
172
|
+
try {
|
|
173
|
+
const data = await etherreq.get('/api/data');
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error('请求失败:', error.message);
|
|
176
|
+
}
|
|
78
177
|
```
|
|
79
178
|
|
|
80
|
-
|
|
179
|
+
## TypeScript 支持
|
|
180
|
+
|
|
181
|
+
完整的类型定义支持:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
interface User {
|
|
185
|
+
id: number;
|
|
186
|
+
name: string;
|
|
187
|
+
email: string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 类型安全的请求
|
|
191
|
+
const user: User = await etherreq.get<User>('/users/1');
|
|
192
|
+
const users: User[] = await etherreq.get<User[]>('/users');
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 浏览器兼容性
|
|
196
|
+
|
|
197
|
+
- Chrome 67+
|
|
198
|
+
- Firefox 61+
|
|
199
|
+
- Safari 12+
|
|
200
|
+
- Edge 79+
|
|
201
|
+
|
|
202
|
+
需要 Promise 和 fetch API 支持。
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生成缓存键的哈希值,避免特殊字符和长度问题
|
|
3
|
+
* @param str 输入字符串
|
|
4
|
+
* @returns 哈希值字符串
|
|
5
|
+
*/
|
|
6
|
+
function hashString(str) {
|
|
7
|
+
let hash = 0;
|
|
8
|
+
for (let i = 0; i < str.length; i++) {
|
|
9
|
+
const char = str.charCodeAt(i);
|
|
10
|
+
hash = ((hash << 5) - hash) + char;
|
|
11
|
+
hash = hash & hash; // 转换为32位整数
|
|
12
|
+
}
|
|
13
|
+
return Math.abs(hash).toString(36);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 检查localStorage是否可用
|
|
17
|
+
* @returns boolean
|
|
18
|
+
*/
|
|
19
|
+
function isLocalStorageAvailable() {
|
|
20
|
+
try {
|
|
21
|
+
const testKey = '__storage_test__';
|
|
22
|
+
localStorage.setItem(testKey, 'test');
|
|
23
|
+
localStorage.removeItem(testKey);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 获取缓存 key(使用哈希算法避免特殊字符)
|
|
32
|
+
* @param url 请求地址
|
|
33
|
+
* @param options 请求参数
|
|
34
|
+
* @returns 缓存键字符串
|
|
35
|
+
*/
|
|
36
|
+
export function getCacheKey(url, options) {
|
|
37
|
+
const rawParams = options.params || {};
|
|
38
|
+
// 将所有值转换为 string
|
|
39
|
+
const stringifiedParams = {};
|
|
40
|
+
for (const [key, value] of Object.entries(rawParams)) {
|
|
41
|
+
stringifiedParams[key] = String(value);
|
|
42
|
+
}
|
|
43
|
+
const params = new URLSearchParams(stringifiedParams);
|
|
44
|
+
const keyString = `${options.method}:${url}?${params.toString()}`;
|
|
45
|
+
return `etherreq_cache_${hashString(keyString)}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 内存缓存存储类
|
|
49
|
+
*/
|
|
50
|
+
class MemoryCache {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.store = new Map();
|
|
53
|
+
this.cleanupTimer = null;
|
|
54
|
+
this.startCleanupInterval();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 启动定期清理过期缓存
|
|
58
|
+
*/
|
|
59
|
+
startCleanupInterval() {
|
|
60
|
+
if (this.cleanupTimer)
|
|
61
|
+
return;
|
|
62
|
+
this.cleanupTimer = setInterval(() => {
|
|
63
|
+
this.cleanupExpired();
|
|
64
|
+
}, 30000); // 每30秒清理一次
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 清理过期缓存
|
|
68
|
+
*/
|
|
69
|
+
cleanupExpired() {
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
for (const [key, entry] of this.store.entries()) {
|
|
72
|
+
if (entry.expireAt <= now) {
|
|
73
|
+
this.store.delete(key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 设置缓存
|
|
79
|
+
*/
|
|
80
|
+
set(key, value, ttl = 5 * 60 * 1000) {
|
|
81
|
+
const expireAt = Date.now() + ttl;
|
|
82
|
+
this.store.set(key, { value, expireAt });
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 获取缓存
|
|
86
|
+
*/
|
|
87
|
+
get(key) {
|
|
88
|
+
const entry = this.store.get(key);
|
|
89
|
+
if (entry && entry.expireAt > Date.now()) {
|
|
90
|
+
return entry.value;
|
|
91
|
+
}
|
|
92
|
+
// 过期则删除
|
|
93
|
+
if (entry) {
|
|
94
|
+
this.store.delete(key);
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 删除指定缓存
|
|
100
|
+
*/
|
|
101
|
+
delete(key) {
|
|
102
|
+
this.store.delete(key);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 清空所有缓存
|
|
106
|
+
*/
|
|
107
|
+
clear() {
|
|
108
|
+
this.store.clear();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 获取缓存大小
|
|
112
|
+
*/
|
|
113
|
+
size() {
|
|
114
|
+
return this.store.size;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 停止清理定时器
|
|
118
|
+
*/
|
|
119
|
+
destroy() {
|
|
120
|
+
if (this.cleanupTimer) {
|
|
121
|
+
clearInterval(this.cleanupTimer);
|
|
122
|
+
this.cleanupTimer = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 本地存储缓存类
|
|
128
|
+
*/
|
|
129
|
+
class LocalStorageCache {
|
|
130
|
+
constructor(prefix = 'etherreq_') {
|
|
131
|
+
this.prefix = prefix;
|
|
132
|
+
this.available = isLocalStorageAvailable();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 设置缓存到localStorage
|
|
136
|
+
*/
|
|
137
|
+
set(key, value, ttl = 5 * 60 * 1000) {
|
|
138
|
+
if (!this.available)
|
|
139
|
+
return;
|
|
140
|
+
try {
|
|
141
|
+
const expireAt = Date.now() + ttl;
|
|
142
|
+
const entry = { value, expireAt };
|
|
143
|
+
localStorage.setItem(`${this.prefix}${key}`, JSON.stringify(entry));
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.warn('LocalStorage缓存设置失败:', error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 从localStorage获取缓存
|
|
151
|
+
*/
|
|
152
|
+
get(key) {
|
|
153
|
+
if (!this.available)
|
|
154
|
+
return null;
|
|
155
|
+
try {
|
|
156
|
+
const item = localStorage.getItem(`${this.prefix}${key}`);
|
|
157
|
+
if (!item)
|
|
158
|
+
return null;
|
|
159
|
+
const entry = JSON.parse(item);
|
|
160
|
+
if (entry.expireAt > Date.now()) {
|
|
161
|
+
return entry.value;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// 过期则删除
|
|
165
|
+
localStorage.removeItem(`${this.prefix}${key}`);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
console.warn('LocalStorage缓存获取失败:', error);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 删除指定缓存
|
|
176
|
+
*/
|
|
177
|
+
delete(key) {
|
|
178
|
+
if (!this.available)
|
|
179
|
+
return;
|
|
180
|
+
try {
|
|
181
|
+
localStorage.removeItem(`${this.prefix}${key}`);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
console.warn('LocalStorage缓存删除失败:', error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 清空所有缓存(只清空当前应用的缓存)
|
|
189
|
+
*/
|
|
190
|
+
clear() {
|
|
191
|
+
if (!this.available)
|
|
192
|
+
return;
|
|
193
|
+
try {
|
|
194
|
+
const keysToRemove = [];
|
|
195
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
196
|
+
const key = localStorage.key(i);
|
|
197
|
+
if (key && key.startsWith(this.prefix)) {
|
|
198
|
+
keysToRemove.push(key);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
console.warn('LocalStorage缓存清空失败:', error);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 复合缓存类 - 结合内存和本地存储
|
|
210
|
+
*/
|
|
211
|
+
class HybridCache {
|
|
212
|
+
constructor() {
|
|
213
|
+
this.memoryCache = new MemoryCache();
|
|
214
|
+
this.localStorageCache = new LocalStorageCache();
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* 设置缓存(同时存储到内存和本地存储)
|
|
218
|
+
*/
|
|
219
|
+
set(key, value, ttl = 5 * 60 * 1000) {
|
|
220
|
+
this.memoryCache.set(key, value, ttl);
|
|
221
|
+
this.localStorageCache.set(key, value, ttl);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 获取缓存(优先从内存获取,否则从本地存储获取)
|
|
225
|
+
*/
|
|
226
|
+
get(key) {
|
|
227
|
+
// 先从内存获取
|
|
228
|
+
let value = this.memoryCache.get(key);
|
|
229
|
+
if (value !== null) {
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
// 内存没有则从本地存储获取
|
|
233
|
+
value = this.localStorageCache.get(key);
|
|
234
|
+
if (value !== null) {
|
|
235
|
+
// 同步到内存缓存
|
|
236
|
+
this.memoryCache.set(key, value);
|
|
237
|
+
return value;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 删除指定缓存
|
|
243
|
+
*/
|
|
244
|
+
delete(key) {
|
|
245
|
+
this.memoryCache.delete(key);
|
|
246
|
+
this.localStorageCache.delete(key);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* 清空所有缓存
|
|
250
|
+
*/
|
|
251
|
+
clear() {
|
|
252
|
+
this.memoryCache.clear();
|
|
253
|
+
this.localStorageCache.clear();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* 销毁缓存实例
|
|
257
|
+
*/
|
|
258
|
+
destroy() {
|
|
259
|
+
this.memoryCache.destroy();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* 缓存工厂类
|
|
264
|
+
*/
|
|
265
|
+
class CacheFactory {
|
|
266
|
+
static create(storageType = 'memory+local') {
|
|
267
|
+
switch (storageType) {
|
|
268
|
+
case 'memory':
|
|
269
|
+
return new MemoryCache();
|
|
270
|
+
case 'local':
|
|
271
|
+
return new LocalStorageCache();
|
|
272
|
+
case 'memory+local':
|
|
273
|
+
default:
|
|
274
|
+
return new HybridCache();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// 默认导出混合缓存实例
|
|
279
|
+
export const requestCache = CacheFactory.create('memory+local');
|
|
280
|
+
// 导出缓存工厂和工具函数
|
|
281
|
+
export { CacheFactory, MemoryCache, LocalStorageCache, HybridCache, hashString, isLocalStorageAvailable };
|
package/dist/etherreq.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// src/etherreq.ts
|
|
2
|
+
export const create = (defaultConfig = {}) => {
|
|
3
|
+
const interceptors = {
|
|
4
|
+
request: [],
|
|
5
|
+
response: [],
|
|
6
|
+
};
|
|
7
|
+
const use = (fulfilled, rejected, type = 'request') => {
|
|
8
|
+
interceptors[type].push({ fulfilled, rejected });
|
|
9
|
+
};
|
|
10
|
+
const dispatchRequest = async (config) => {
|
|
11
|
+
// 请求拦截器执行
|
|
12
|
+
for (const interceptor of interceptors.request) {
|
|
13
|
+
const nextConfig = await interceptor.fulfilled(config);
|
|
14
|
+
// ✅ 拦截器可以返回一个 response 对象,表示中断请求
|
|
15
|
+
if (nextConfig && nextConfig.isFromCache) {
|
|
16
|
+
return nextConfig.data; // ✅ 直接返回缓存结果,不再往下执行
|
|
17
|
+
}
|
|
18
|
+
config = nextConfig;
|
|
19
|
+
}
|
|
20
|
+
// 检查 url 是否存在
|
|
21
|
+
if (!config.url) {
|
|
22
|
+
throw new Error('请求配置中缺少 url 属性');
|
|
23
|
+
}
|
|
24
|
+
let response;
|
|
25
|
+
const { url, method = 'GET', headers = {}, body } = config;
|
|
26
|
+
let text = '';
|
|
27
|
+
try {
|
|
28
|
+
const options = {
|
|
29
|
+
method,
|
|
30
|
+
headers,
|
|
31
|
+
body: method !== 'GET' ? JSON.stringify(body) : undefined,
|
|
32
|
+
};
|
|
33
|
+
const res = await fetch(url, options);
|
|
34
|
+
text = await res.text();
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
throw new Error(`HTTP 错误: ${res.status} - ${res.statusText}`);
|
|
37
|
+
}
|
|
38
|
+
if (text.trim().startsWith('<')) {
|
|
39
|
+
throw new Error('服务器返回了 HTML 页面,预期为 JSON 格式');
|
|
40
|
+
}
|
|
41
|
+
const data = JSON.parse(text);
|
|
42
|
+
response = {
|
|
43
|
+
data,
|
|
44
|
+
status: res.status,
|
|
45
|
+
statusText: res.statusText,
|
|
46
|
+
headers: Object.fromEntries(res.headers.entries()),
|
|
47
|
+
config,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
error.message += `\n请求地址: ${url}`;
|
|
52
|
+
if (text) {
|
|
53
|
+
error.message += `\n响应内容: ${text.substring(0, 200)}...`;
|
|
54
|
+
}
|
|
55
|
+
for (const interceptor of interceptors.response) {
|
|
56
|
+
if (interceptor.rejected) {
|
|
57
|
+
return interceptor.rejected(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
// 响应拦截器执行
|
|
63
|
+
for (const interceptor of interceptors.response) {
|
|
64
|
+
response = await interceptor.fulfilled(response);
|
|
65
|
+
}
|
|
66
|
+
return response;
|
|
67
|
+
};
|
|
68
|
+
const instance = (config) => {
|
|
69
|
+
return dispatchRequest({
|
|
70
|
+
...defaultConfig,
|
|
71
|
+
...config,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
instance.use = (fulfilled, rejected) => use(fulfilled, rejected, 'request');
|
|
75
|
+
instance.interceptors = {
|
|
76
|
+
request: {
|
|
77
|
+
use: (fulfilled, rejected) => use(fulfilled, rejected, 'request'),
|
|
78
|
+
},
|
|
79
|
+
response: {
|
|
80
|
+
use: (fulfilled, rejected) => use(fulfilled, rejected, 'response'),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
return instance;
|
|
84
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { request, baseURL as _baseURL, setBaseURL } from './request';
|
|
3
|
+
// 示例封装方法
|
|
4
|
+
const createMethod = (method) => (url, data, callback) => {
|
|
5
|
+
let options;
|
|
6
|
+
if (data &&
|
|
7
|
+
typeof data === 'object' &&
|
|
8
|
+
!Array.isArray(data) &&
|
|
9
|
+
!(data instanceof FormData) &&
|
|
10
|
+
!data.method &&
|
|
11
|
+
!data.headers &&
|
|
12
|
+
!data.params &&
|
|
13
|
+
!data.baseURL) {
|
|
14
|
+
options = { body: data, method: method };
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
options = { ...data, method: method };
|
|
18
|
+
}
|
|
19
|
+
const promise = request(url, options);
|
|
20
|
+
if (typeof callback === 'function') {
|
|
21
|
+
promise.then(data => callback(null, data)).catch(err => callback(err, null));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return promise;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export const etherreq = Object.assign((url, options, callback) => createMethod('GET')(url, options, callback), {
|
|
28
|
+
get: createMethod('GET'),
|
|
29
|
+
post: createMethod('POST'),
|
|
30
|
+
put: createMethod('PUT'),
|
|
31
|
+
delete: createMethod('DELETE'),
|
|
32
|
+
del: createMethod('DELETE'),
|
|
33
|
+
head: createMethod('HEAD'),
|
|
34
|
+
options: createMethod('OPTIONS'),
|
|
35
|
+
patch: createMethod('PATCH'),
|
|
36
|
+
login: (url, data, callback) => {
|
|
37
|
+
// 确保总是获得Promise对象,即使提供了callback
|
|
38
|
+
const loginPromise = createMethod('POST')(url, data);
|
|
39
|
+
// 如果createMethod返回undefined(提供了callback的情况),创建一个新的Promise
|
|
40
|
+
const promiseToUse = loginPromise instanceof Promise ? loginPromise : request(url, {
|
|
41
|
+
...(typeof data === 'object' && !Array.isArray(data) && !(data instanceof FormData) ? { body: data } : data),
|
|
42
|
+
method: 'POST'
|
|
43
|
+
});
|
|
44
|
+
promiseToUse.then((response) => {
|
|
45
|
+
const token = response.data?.token;
|
|
46
|
+
if (token) {
|
|
47
|
+
localStorage.setItem('token', token); // 保存 token 到 localStorage
|
|
48
|
+
}
|
|
49
|
+
return response;
|
|
50
|
+
});
|
|
51
|
+
if (typeof callback === 'function') {
|
|
52
|
+
promiseToUse.then(data => callback(null, data)).catch(err => callback(err, null));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return promiseToUse;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
export { setBaseURL, _baseURL as baseURL };
|