@qlover/create-app 0.6.2 → 0.6.3
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/CHANGELOG.md +35 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/react-app/README.en.md +257 -0
- package/dist/templates/react-app/README.md +29 -231
- package/dist/templates/react-app/docs/en/bootstrap.md +562 -0
- package/dist/templates/react-app/docs/en/development-guide.md +523 -0
- package/dist/templates/react-app/docs/en/env.md +482 -0
- package/dist/templates/react-app/docs/en/global.md +509 -0
- package/dist/templates/react-app/docs/en/i18n.md +268 -0
- package/dist/templates/react-app/docs/en/index.md +173 -0
- package/dist/templates/react-app/docs/en/ioc.md +424 -0
- package/dist/templates/react-app/docs/en/project-structure.md +434 -0
- package/dist/templates/react-app/docs/en/request.md +425 -0
- package/dist/templates/react-app/docs/en/router.md +404 -0
- package/dist/templates/react-app/docs/en/store.md +321 -0
- package/dist/templates/react-app/docs/en/theme.md +424 -0
- package/dist/templates/react-app/docs/en/typescript-guide.md +473 -0
- package/dist/templates/react-app/docs/zh/bootstrap.md +7 -0
- package/dist/templates/react-app/docs/zh/development-guide.md +523 -0
- package/dist/templates/react-app/docs/zh/env.md +24 -25
- package/dist/templates/react-app/docs/zh/global.md +28 -27
- package/dist/templates/react-app/docs/zh/i18n.md +268 -0
- package/dist/templates/react-app/docs/zh/index.md +173 -0
- package/dist/templates/react-app/docs/zh/ioc.md +44 -32
- package/dist/templates/react-app/docs/zh/project-structure.md +434 -0
- package/dist/templates/react-app/docs/zh/request.md +429 -0
- package/dist/templates/react-app/docs/zh/router.md +408 -0
- package/dist/templates/react-app/docs/zh/store.md +321 -0
- package/dist/templates/react-app/docs/zh/theme.md +424 -0
- package/dist/templates/react-app/docs/zh/typescript-guide.md +473 -0
- package/dist/templates/react-app/package.json +1 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/dark.css +3 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/index.css +1 -1
- package/dist/templates/react-app/src/styles/css/antd-themes/pink.css +6 -1
- package/dist/templates/react-app/src/styles/css/page.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# 请求系统
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
请求系统基于 `@qlover/fe-corekit` 的 request 模块实现,提供了一个功能强大、可扩展的 HTTP 请求解决方案。系统采用插件化架构,支持请求拦截、响应处理、错误处理等功能。
|
|
6
|
+
|
|
7
|
+
## 核心特性
|
|
8
|
+
|
|
9
|
+
- 🔌 **插件化架构**:支持自定义插件扩展
|
|
10
|
+
- 🔄 **请求调度**:统一的请求调度管理
|
|
11
|
+
- 🛡️ **类型安全**:完整的 TypeScript 支持
|
|
12
|
+
- 🎯 **请求中断**:支持请求取消
|
|
13
|
+
- 📝 **日志记录**:详细的请求日志
|
|
14
|
+
- 🔧 **Mock 支持**:内置数据 Mock 功能
|
|
15
|
+
|
|
16
|
+
## 基础架构
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// 请求系统架构
|
|
20
|
+
Request → Scheduler → Adapter → Plugins → Network
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 使用方式
|
|
24
|
+
|
|
25
|
+
### 1. 创建 API 服务
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { RequestAdapterFetch, RequestScheduler } from '@qlover/fe-corekit';
|
|
29
|
+
|
|
30
|
+
@injectable()
|
|
31
|
+
export class FeApi extends RequestScheduler<FeApiConfig> {
|
|
32
|
+
constructor(@inject(FeApiAdapter) adapter: RequestAdapterFetch) {
|
|
33
|
+
super(adapter);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// API 方法定义
|
|
37
|
+
async getIpInfo(): Promise<FeApiGetIpInfo['response']> {
|
|
38
|
+
return this.get('http://ip-api.com/json/');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. 配置请求适配器
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { RequestAdapterFetch } from '@qlover/fe-corekit';
|
|
47
|
+
|
|
48
|
+
const apiAdapter = new RequestAdapterFetch({
|
|
49
|
+
responseType: 'json',
|
|
50
|
+
baseURL: '/api'
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// 配置插件
|
|
54
|
+
apiAdapter.usePlugin(new FetchURLPlugin());
|
|
55
|
+
apiAdapter.usePlugin(
|
|
56
|
+
new RequestCommonPlugin({
|
|
57
|
+
tokenPrefix: 'Bearer',
|
|
58
|
+
token: () => getToken()
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. 注册请求服务
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// core/registers/RegisterCommon.ts
|
|
67
|
+
export const RegisterCommon: IOCRegister = {
|
|
68
|
+
register(container) {
|
|
69
|
+
// 注册请求插件
|
|
70
|
+
const feApiAbort = new FetchAbortPlugin();
|
|
71
|
+
const feApiRequestCommonPlugin = new RequestCommonPlugin({
|
|
72
|
+
tokenPrefix: AppConfig.openAiTokenPrefix,
|
|
73
|
+
requiredToken: true,
|
|
74
|
+
token: () => container.get(UserService).getToken()
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
container.bind(FetchAbortPlugin, feApiAbort);
|
|
78
|
+
container.bind(IOCIdentifier.FeApiCommonPlugin, feApiRequestCommonPlugin);
|
|
79
|
+
container.bind(
|
|
80
|
+
IOCIdentifier.ApiMockPlugin,
|
|
81
|
+
new ApiMockPlugin(mockDataJson, container.get(Logger))
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 4. 在组件中使用
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
@injectable()
|
|
91
|
+
export class RequestController extends StoreInterface<RequestControllerState> {
|
|
92
|
+
constructor(
|
|
93
|
+
@inject(FeApi) private readonly feApi: FeApi,
|
|
94
|
+
@inject(UserApi) private readonly userApi: UserApi
|
|
95
|
+
) {
|
|
96
|
+
super(createDefaultState);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async fetchData() {
|
|
100
|
+
if (this.state.loading) return;
|
|
101
|
+
|
|
102
|
+
this.setState({ loading: true });
|
|
103
|
+
try {
|
|
104
|
+
const result = await this.feApi.getData();
|
|
105
|
+
this.setState({ loading: false, data: result });
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.setState({ loading: false, error });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 插件系统
|
|
114
|
+
|
|
115
|
+
### 1. 内置插件
|
|
116
|
+
|
|
117
|
+
#### FetchURLPlugin
|
|
118
|
+
|
|
119
|
+
- URL 处理和构建
|
|
120
|
+
- 参数序列化
|
|
121
|
+
- baseURL 处理
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
apiAdapter.usePlugin(new FetchURLPlugin());
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### RequestCommonPlugin
|
|
128
|
+
|
|
129
|
+
- 通用请求头设置
|
|
130
|
+
- Token 管理
|
|
131
|
+
- 认证信息处理
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
apiAdapter.usePlugin(
|
|
135
|
+
new RequestCommonPlugin({
|
|
136
|
+
tokenPrefix: 'Bearer',
|
|
137
|
+
requiredToken: true,
|
|
138
|
+
token: () => getToken()
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### ApiMockPlugin
|
|
144
|
+
|
|
145
|
+
- Mock 数据支持
|
|
146
|
+
- 开发环境调试
|
|
147
|
+
- 接口模拟
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
apiAdapter.usePlugin(new ApiMockPlugin(mockDataJson, logger));
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### RequestLogger
|
|
154
|
+
|
|
155
|
+
- 请求日志记录
|
|
156
|
+
- 错误追踪
|
|
157
|
+
- 调试信息
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
@injectable()
|
|
161
|
+
export class RequestLogger
|
|
162
|
+
implements ExecutorPlugin<RequestAdapterFetchConfig>
|
|
163
|
+
{
|
|
164
|
+
readonly pluginName = 'RequestLogger';
|
|
165
|
+
|
|
166
|
+
onBefore(context) {
|
|
167
|
+
this.logger.log(`Request: ${context.parameters.url}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
onSuccess(context) {
|
|
171
|
+
this.logger.log(`Success: ${context.parameters.url}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
onError(context) {
|
|
175
|
+
this.logger.error(`Error: ${context.parameters.url}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 2. 自定义插件
|
|
181
|
+
|
|
182
|
+
创建自定义插件:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
export class CustomPlugin implements ExecutorPlugin<RequestAdapterFetchConfig> {
|
|
186
|
+
readonly pluginName = 'CustomPlugin';
|
|
187
|
+
|
|
188
|
+
onBefore(context) {
|
|
189
|
+
// 请求前处理
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
onSuccess(context) {
|
|
193
|
+
// 请求成功处理
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
onError(context) {
|
|
197
|
+
// 请求失败处理
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## 请求配置
|
|
203
|
+
|
|
204
|
+
### 1. 基础配置
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
interface RequestConfig {
|
|
208
|
+
url: string;
|
|
209
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
210
|
+
headers?: Record<string, string>;
|
|
211
|
+
params?: Record<string, any>;
|
|
212
|
+
data?: any;
|
|
213
|
+
timeout?: number;
|
|
214
|
+
responseType?: 'json' | 'text' | 'blob';
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 2. 高级配置
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
interface AdvancedConfig extends RequestConfig {
|
|
222
|
+
requestId?: string; // 用于请求取消
|
|
223
|
+
mock?: boolean; // 启用 Mock 数据
|
|
224
|
+
retries?: number; // 重试次数
|
|
225
|
+
withCredentials?: boolean; // 跨域携带凭证
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## 错误处理
|
|
230
|
+
|
|
231
|
+
### 1. 统一错误处理
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
export class ApiCatchPlugin implements ExecutorPlugin {
|
|
235
|
+
onError(context) {
|
|
236
|
+
const error = context.error;
|
|
237
|
+
|
|
238
|
+
if (error.status === 401) {
|
|
239
|
+
// 处理认证错误
|
|
240
|
+
handleAuthError();
|
|
241
|
+
} else if (error.status === 500) {
|
|
242
|
+
// 处理服务器错误
|
|
243
|
+
handleServerError();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 2. 业务层错误处理
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
try {
|
|
253
|
+
const result = await api.getData();
|
|
254
|
+
handleSuccess(result);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (error instanceof ApiError) {
|
|
257
|
+
handleApiError(error);
|
|
258
|
+
} else {
|
|
259
|
+
handleUnexpectedError(error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## 最佳实践
|
|
265
|
+
|
|
266
|
+
### 1. API 组织
|
|
267
|
+
|
|
268
|
+
- 按功能模块组织 API
|
|
269
|
+
- 使用统一的响应类型
|
|
270
|
+
- 实现完整的类型定义
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// 用户相关 API
|
|
274
|
+
export class UserApi extends RequestScheduler {
|
|
275
|
+
login(data: LoginData): Promise<LoginResponse> {}
|
|
276
|
+
register(data: RegisterData): Promise<RegisterResponse> {}
|
|
277
|
+
getUserInfo(): Promise<UserInfo> {}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 订单相关 API
|
|
281
|
+
export class OrderApi extends RequestScheduler {
|
|
282
|
+
createOrder(data: OrderData): Promise<OrderResponse> {}
|
|
283
|
+
getOrderList(): Promise<OrderList> {}
|
|
284
|
+
cancelOrder(id: string): Promise<void> {}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 2. 请求状态管理
|
|
289
|
+
|
|
290
|
+
- 使用 Store 管理请求状态
|
|
291
|
+
- 实现加载状态控制
|
|
292
|
+
- 处理并发请求
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
interface RequestState<T> {
|
|
296
|
+
loading: boolean;
|
|
297
|
+
data: T | null;
|
|
298
|
+
error: Error | null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
class DataStore extends StoreInterface<RequestState<Data>> {
|
|
302
|
+
async fetchData() {
|
|
303
|
+
if (this.state.loading) return;
|
|
304
|
+
|
|
305
|
+
this.setState({ loading: true });
|
|
306
|
+
try {
|
|
307
|
+
const data = await api.getData();
|
|
308
|
+
this.setState({ loading: false, data });
|
|
309
|
+
} catch (error) {
|
|
310
|
+
this.setState({ loading: false, error });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 3. 请求缓存
|
|
317
|
+
|
|
318
|
+
- 实现请求结果缓存
|
|
319
|
+
- 避免重复请求
|
|
320
|
+
- 管理缓存生命周期
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
class CachePlugin implements ExecutorPlugin {
|
|
324
|
+
private cache = new Map();
|
|
325
|
+
|
|
326
|
+
onBefore(context) {
|
|
327
|
+
const cached = this.cache.get(context.parameters.url);
|
|
328
|
+
if (cached && !isExpired(cached)) {
|
|
329
|
+
return cached.data;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
onSuccess(context) {
|
|
334
|
+
this.cache.set(context.parameters.url, {
|
|
335
|
+
data: context.returnValue,
|
|
336
|
+
timestamp: Date.now()
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## 调试与测试
|
|
343
|
+
|
|
344
|
+
### 1. 开发环境配置
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// 开发环境配置
|
|
348
|
+
const devConfig = {
|
|
349
|
+
baseURL: '/api',
|
|
350
|
+
timeout: 5000,
|
|
351
|
+
mock: true,
|
|
352
|
+
debug: true
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// 生产环境配置
|
|
356
|
+
const prodConfig = {
|
|
357
|
+
baseURL: 'https://api.example.com',
|
|
358
|
+
timeout: 10000,
|
|
359
|
+
mock: false,
|
|
360
|
+
debug: false
|
|
361
|
+
};
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 2. Mock 数据配置
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"GET /api/users": {
|
|
369
|
+
"code": 0,
|
|
370
|
+
"data": [
|
|
371
|
+
{ "id": 1, "name": "User 1" },
|
|
372
|
+
{ "id": 2, "name": "User 2" }
|
|
373
|
+
]
|
|
374
|
+
},
|
|
375
|
+
"POST /api/login": {
|
|
376
|
+
"code": 0,
|
|
377
|
+
"data": {
|
|
378
|
+
"token": "mock-token",
|
|
379
|
+
"user": { "id": 1, "name": "User" }
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 3. 请求测试
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
describe('UserApi', () => {
|
|
389
|
+
let api: UserApi;
|
|
390
|
+
|
|
391
|
+
beforeEach(() => {
|
|
392
|
+
api = new UserApi(new MockAdapter());
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('should login successfully', async () => {
|
|
396
|
+
const result = await api.login({
|
|
397
|
+
username: 'test',
|
|
398
|
+
password: '123456'
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
expect(result.code).toBe(0);
|
|
402
|
+
expect(result.data.token).toBeDefined();
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## 性能优化
|
|
408
|
+
|
|
409
|
+
### 1. 请求优化
|
|
410
|
+
|
|
411
|
+
- 实现请求合并
|
|
412
|
+
- 控制并发数量
|
|
413
|
+
- 优化请求时机
|
|
414
|
+
|
|
415
|
+
### 2. 缓存策略
|
|
416
|
+
|
|
417
|
+
- 合理使用缓存
|
|
418
|
+
- 实现缓存更新
|
|
419
|
+
- 管理缓存大小
|
|
420
|
+
|
|
421
|
+
### 3. 错误重试
|
|
422
|
+
|
|
423
|
+
- 配置重试策略
|
|
424
|
+
- 处理网络问题
|
|
425
|
+
- 避免无效重试
|
|
426
|
+
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
```
|