vite-plugin-automock 1.1.6 → 1.2.0
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 +11 -25
- package/dist/{chunk-DJWYFFPU.mjs → chunk-2KRAWI7J.mjs} +35 -11
- package/dist/chunk-2KRAWI7J.mjs.map +1 -0
- package/dist/client/index.d.mts +3 -2
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.js +34 -10
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +65 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -10
- package/dist/index.mjs.map +1 -1
- package/docs/API_CN.md +398 -0
- package/docs/CONFIGURATION_CN.md +196 -0
- package/package.json +3 -2
- package/dist/chunk-DJWYFFPU.mjs.map +0 -1
package/docs/API_CN.md
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# API 文档 / API Reference
|
|
2
|
+
|
|
3
|
+
## 客户端拦截器 API / Client Interceptor API
|
|
4
|
+
|
|
5
|
+
### 导出方法 / Exported Functions
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
#### `initMockInterceptor(axiosInstance)`
|
|
10
|
+
|
|
11
|
+
初始化通用 Axios 拦截器
|
|
12
|
+
|
|
13
|
+
Initialize a generic Axios mock interceptor
|
|
14
|
+
|
|
15
|
+
**参数 / Parameters:**
|
|
16
|
+
|
|
17
|
+
| 参数名 | 类型 | 必填 | 说明 |
|
|
18
|
+
|--------|------|------|------|
|
|
19
|
+
| `axiosInstance` | `AxiosInstance` | ✅ | Axios 实例对象 |
|
|
20
|
+
|
|
21
|
+
**返回值 / Returns:** `Promise<void>`
|
|
22
|
+
|
|
23
|
+
**使用示例 / Usage Example:**
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { initMockInterceptor } from 'vite-plugin-api-mmock/client'
|
|
27
|
+
import axios from 'axios'
|
|
28
|
+
|
|
29
|
+
await initMockInterceptor(axios)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
#### `initMockInterceptorForPureHttp()`
|
|
35
|
+
|
|
36
|
+
初始化 PureHttp 封装的拦截器
|
|
37
|
+
|
|
38
|
+
Initialize mock interceptor for PureHttp wrapper
|
|
39
|
+
|
|
40
|
+
**参数 / Parameters:** 无
|
|
41
|
+
|
|
42
|
+
**返回值 / Returns:** `Promise<void>`
|
|
43
|
+
|
|
44
|
+
**使用示例 / Usage Example:**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import {
|
|
48
|
+
initMockInterceptorForPureHttp,
|
|
49
|
+
registerHttpInstance
|
|
50
|
+
} from 'vite-plugin-api-mmock/client'
|
|
51
|
+
import { http } from './http'
|
|
52
|
+
|
|
53
|
+
// 必须先注册 http 实例
|
|
54
|
+
registerHttpInstance(http)
|
|
55
|
+
|
|
56
|
+
// 然后初始化拦截器
|
|
57
|
+
await initMockInterceptorForPureHttp()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
#### `createMockInterceptor(options)`
|
|
63
|
+
|
|
64
|
+
创建自定义拦截器实例
|
|
65
|
+
|
|
66
|
+
Create a custom mock interceptor instance
|
|
67
|
+
|
|
68
|
+
**参数 / Parameters:**
|
|
69
|
+
|
|
70
|
+
| 参数名 | 类型 | 必填 | 说明 |
|
|
71
|
+
|--------|------|------|------|
|
|
72
|
+
| `options` | `MockInterceptorOptions` | ✅ | 拦截器配置对象 |
|
|
73
|
+
|
|
74
|
+
**MockInterceptorOptions 类型定义:**
|
|
75
|
+
|
|
76
|
+
| 属性 | 类型 | 必填 | 说明 |
|
|
77
|
+
|------|------|------|------|
|
|
78
|
+
| `mockData` | `Record<string, MockBundleData>` | ✅ | Mock 数据对象,key 为请求标识 |
|
|
79
|
+
| `enabled` | `boolean \| (() => boolean)` | ❌ | 是否启用 mock,默认 false |
|
|
80
|
+
| `onMockHit` | `(url, method, mock) => void` | ❌ | Mock 命中时的回调 |
|
|
81
|
+
| `onBypass` | `(url, method, reason) => void` | ❌ | Mock 跳过时的回调 |
|
|
82
|
+
|
|
83
|
+
**返回值 / Returns:** `MockInterceptor` 实例
|
|
84
|
+
|
|
85
|
+
**使用示例 / Usage Example:**
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { createMockInterceptor } from 'vite-plugin-api-mmock/client'
|
|
89
|
+
|
|
90
|
+
const interceptor = createMockInterceptor({
|
|
91
|
+
mockData: {
|
|
92
|
+
'/api/users/get.js': {
|
|
93
|
+
enable: true,
|
|
94
|
+
data: { users: [{ id: 1, name: 'Alice' }] },
|
|
95
|
+
delay: 100,
|
|
96
|
+
status: 200
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
enabled: true,
|
|
100
|
+
onMockHit: (url, method, mock) => {
|
|
101
|
+
console.log(`[MOCK HIT] ${method} ${url}`)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
interceptor.setupAxios(axiosInstance)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
#### `setMockEnabled(enabled)`
|
|
111
|
+
|
|
112
|
+
设置运行时 Mock 开关状态
|
|
113
|
+
|
|
114
|
+
Set mock enabled state at runtime
|
|
115
|
+
|
|
116
|
+
**参数 / Parameters:**
|
|
117
|
+
|
|
118
|
+
| 参数名 | 类型 | 必填 | 说明 |
|
|
119
|
+
|--------|------|------|------|
|
|
120
|
+
| `enabled` | `boolean` | ✅ | true 启用,false 禁用 |
|
|
121
|
+
|
|
122
|
+
**返回值 / Returns:** `void`
|
|
123
|
+
|
|
124
|
+
**使用示例 / Usage Example:**
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { setMockEnabled } from 'vite-plugin-api-mmock/client'
|
|
128
|
+
|
|
129
|
+
// 启用 mock
|
|
130
|
+
setMockEnabled(true)
|
|
131
|
+
|
|
132
|
+
// 禁用 mock
|
|
133
|
+
setMockEnabled(false)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
#### `isMockEnabled()`
|
|
139
|
+
|
|
140
|
+
获取当前 Mock 开关状态
|
|
141
|
+
|
|
142
|
+
Get current mock enabled state
|
|
143
|
+
|
|
144
|
+
**参数 / Parameters:** 无
|
|
145
|
+
|
|
146
|
+
**返回值 / Returns:** `boolean`
|
|
147
|
+
|
|
148
|
+
**使用示例 / Usage Example:**
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { isMockEnabled } from 'vite-plugin-api-mmock/client'
|
|
152
|
+
|
|
153
|
+
if (isMockEnabled()) {
|
|
154
|
+
console.log('Mock is currently enabled')
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
#### `registerHttpInstance(http)`
|
|
161
|
+
|
|
162
|
+
注册 PureHttp 实例(PureHttp 专用)
|
|
163
|
+
|
|
164
|
+
Register PureHttp instance (for PureHttp only)
|
|
165
|
+
|
|
166
|
+
**参数 / Parameters:**
|
|
167
|
+
|
|
168
|
+
| 参数名 | 类型 | 必填 | 说明 |
|
|
169
|
+
|--------|------|------|------|
|
|
170
|
+
| `http` | `HttpInstance` | ✅ | PureHttp 实例对象 |
|
|
171
|
+
|
|
172
|
+
**HttpInstance 类型要求:**
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface HttpInstance {
|
|
176
|
+
constructor: {
|
|
177
|
+
axiosInstance: AxiosInstance
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**返回值 / Returns:** `void`
|
|
183
|
+
|
|
184
|
+
**使用示例 / Usage Example:**
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { registerHttpInstance } from 'vite-plugin-api-mmock/client'
|
|
188
|
+
import { http } from './http'
|
|
189
|
+
|
|
190
|
+
registerHttpInstance(http)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
#### `loadMockData()`
|
|
196
|
+
|
|
197
|
+
从服务器加载 mock 数据文件
|
|
198
|
+
|
|
199
|
+
Load mock data from server
|
|
200
|
+
|
|
201
|
+
**参数 / Parameters:** 无
|
|
202
|
+
|
|
203
|
+
**返回值 / Returns:** `Promise<Record<string, MockBundleData>>`
|
|
204
|
+
|
|
205
|
+
**使用示例 / Usage Example:**
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { loadMockData } from 'vite-plugin-api-mmock/client'
|
|
209
|
+
|
|
210
|
+
const mockData = await loadMockData()
|
|
211
|
+
console.log('Loaded mock data:', mockData)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 类型定义 / Type Definitions
|
|
217
|
+
|
|
218
|
+
### MockBundleData
|
|
219
|
+
|
|
220
|
+
单个 Mock 数据结构
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
interface MockBundleData {
|
|
224
|
+
enable: boolean // 是否启用此 mock
|
|
225
|
+
data: unknown // 响应数据
|
|
226
|
+
delay?: number // 延迟毫秒数
|
|
227
|
+
status?: number // HTTP 状态码
|
|
228
|
+
isBinary?: boolean // 是否为二进制文件
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### MockInterceptorOptions
|
|
233
|
+
|
|
234
|
+
拦截器配置选项
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
interface MockInterceptorOptions {
|
|
238
|
+
mockData: Record<string, MockBundleData>
|
|
239
|
+
enabled?: boolean | (() => boolean)
|
|
240
|
+
onMockHit?: (url: string, method: string, mock: MockBundleData) => void
|
|
241
|
+
onBypass?: (url: string, method: string, reason: string) => void
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Mock 数据格式 / Mock Data Format
|
|
248
|
+
|
|
249
|
+
### Key 格式 / Key Format
|
|
250
|
+
|
|
251
|
+
支持两种格式:
|
|
252
|
+
|
|
253
|
+
1. **文件路径格式**: `/api/users/get.js`
|
|
254
|
+
2. **HTTP 方法格式**: `GET /api/users`
|
|
255
|
+
|
|
256
|
+
### 完整示例 / Complete Example
|
|
257
|
+
|
|
258
|
+
```json
|
|
259
|
+
{
|
|
260
|
+
"/api/users/get.js": {
|
|
261
|
+
"enable": true,
|
|
262
|
+
"data": {
|
|
263
|
+
"code": 0,
|
|
264
|
+
"message": "success",
|
|
265
|
+
"data": [
|
|
266
|
+
{ "id": 1, "name": "Alice" },
|
|
267
|
+
{ "id": 2, "name": "Bob" }
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
"delay": 100,
|
|
271
|
+
"status": 200
|
|
272
|
+
},
|
|
273
|
+
"POST /api/login": {
|
|
274
|
+
"enable": true,
|
|
275
|
+
"data": {
|
|
276
|
+
"code": 0,
|
|
277
|
+
"message": "Login success",
|
|
278
|
+
"data": { "token": "xxx" }
|
|
279
|
+
},
|
|
280
|
+
"delay": 500,
|
|
281
|
+
"status": 200
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 环境变量 / Environment Variables
|
|
289
|
+
|
|
290
|
+
### VITE_USE_MOCK
|
|
291
|
+
|
|
292
|
+
构建时配置 Mock 默认状态
|
|
293
|
+
|
|
294
|
+
Configure default mock state at build time
|
|
295
|
+
|
|
296
|
+
| 值 | 说明 |
|
|
297
|
+
|----|------|
|
|
298
|
+
| `'true'` | 默认启用 Mock |
|
|
299
|
+
| 未设置或其他值 | 默认禁用 Mock |
|
|
300
|
+
|
|
301
|
+
**使用方式:**
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# .env
|
|
305
|
+
VITE_USE_MOCK=true
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
// 代码中会自动读取
|
|
310
|
+
const isEnvEnabled = import.meta.env.VITE_USE_MOCK === 'true'
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## 优先级 / Priority
|
|
316
|
+
|
|
317
|
+
运行时控制的优先级高于环境变量
|
|
318
|
+
|
|
319
|
+
Runtime control has higher priority than environment variables
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
setMockEnabled(true) > VITE_USE_MOCK='true' > enabled: false (配置)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 完整使用示例 / Complete Usage Example
|
|
328
|
+
|
|
329
|
+
### PureHttp + Vite
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// vite.config.ts
|
|
333
|
+
import { automock } from 'vite-plugin-api-mmock'
|
|
334
|
+
|
|
335
|
+
export default {
|
|
336
|
+
plugins: [
|
|
337
|
+
automock({
|
|
338
|
+
mockDir: 'mock',
|
|
339
|
+
bundleMockData: true
|
|
340
|
+
})
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/api/index.ts
|
|
345
|
+
import {
|
|
346
|
+
initMockInterceptorForPureHttp,
|
|
347
|
+
setMockEnabled,
|
|
348
|
+
registerHttpInstance
|
|
349
|
+
} from 'vite-plugin-api-mmock/client'
|
|
350
|
+
import { http } from './http'
|
|
351
|
+
|
|
352
|
+
// 注册实例
|
|
353
|
+
registerHttpInstance(http)
|
|
354
|
+
|
|
355
|
+
// 初始化拦截器
|
|
356
|
+
initMockInterceptorForPureHttp()
|
|
357
|
+
.then(() => console.log('[Mock] Initialized'))
|
|
358
|
+
.catch(err => console.error('[Mock] Failed:', err))
|
|
359
|
+
|
|
360
|
+
// 开发环境启用
|
|
361
|
+
if (import.meta.env.DEV) {
|
|
362
|
+
setMockEnabled(true)
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Axios + Vite
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// vite.config.ts
|
|
370
|
+
import { automock } from 'vite-plugin-api-mmock'
|
|
371
|
+
|
|
372
|
+
export default {
|
|
373
|
+
plugins: [
|
|
374
|
+
automock({
|
|
375
|
+
mockDir: 'mock',
|
|
376
|
+
bundleMockData: true
|
|
377
|
+
})
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/main.ts
|
|
382
|
+
import { initMockInterceptor, setMockEnabled } from 'vite-plugin-api-mmock/client'
|
|
383
|
+
import axios from 'axios'
|
|
384
|
+
|
|
385
|
+
// 初始化拦截器
|
|
386
|
+
initMockInterceptor(axios)
|
|
387
|
+
.then(() => console.log('[Mock] Initialized'))
|
|
388
|
+
.catch(err => console.error('[Mock] Failed:', err))
|
|
389
|
+
|
|
390
|
+
// 开发环境启用
|
|
391
|
+
if (import.meta.env.DEV) {
|
|
392
|
+
setMockEnabled(true)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// 运行时动态控制
|
|
396
|
+
setMockEnabled(false) // 切换到真实 API
|
|
397
|
+
setMockEnabled(true) // 切换回 Mock
|
|
398
|
+
```
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# vite-plugin-automock 配置指南
|
|
2
|
+
|
|
3
|
+
这份文档只解释“怎么配”和“配置之间怎么相互影响”。API 用法和 mock 文件格式见 README。
|
|
4
|
+
|
|
5
|
+
## 推荐先从最小配置开始
|
|
6
|
+
|
|
7
|
+
大多数开发期录制 mock 的项目只需要:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { defineConfig } from 'vite'
|
|
11
|
+
import { automock } from 'vite-plugin-automock'
|
|
12
|
+
|
|
13
|
+
export default defineConfig({
|
|
14
|
+
plugins: [
|
|
15
|
+
automock({
|
|
16
|
+
proxyBaseUrl: 'https://api.example.com',
|
|
17
|
+
pathRewrite: (path) => path.replace(/^\/api/, ''),
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
这套配置会处理默认的 `/api` 前缀:
|
|
24
|
+
|
|
25
|
+
1. 如果本地存在启用的 mock 文件,直接返回 mock。
|
|
26
|
+
2. 如果没有命中 mock,请求会代理到 `proxyBaseUrl`。
|
|
27
|
+
3. 如果代理响应是 JSON,会生成本地 mock 文件;非 JSON/下载流会直接透传,不自动捕获。
|
|
28
|
+
|
|
29
|
+
不要同时给同一个前缀配置 Vite `server.proxy`。如果使用 `proxyBaseUrl`,代理由 automock 处理。
|
|
30
|
+
|
|
31
|
+
## 配置分层
|
|
32
|
+
|
|
33
|
+
### 基础路径
|
|
34
|
+
|
|
35
|
+
| 配置 | 类型 | 默认值 | 作用 |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| `apiPrefix` | `string` | `'/api'` | 插件处理的请求路径前缀。只匹配完整路径段,例如 `/api` 和 `/api/users`,不会匹配 `/apiary`。 |
|
|
38
|
+
| `mockDir` | `string` | `'mock'` | mock 文件目录。相对路径基于运行 Vite 的当前工作目录。 |
|
|
39
|
+
| `pathRewrite` | `(path: string) => string` | `(path) => path` | 转发到真实后端前改写请求路径。常用于把 `/api/users` 转成 `/users`。 |
|
|
40
|
+
| `proxyBaseUrl` | `string` | 无 | 真实后端地址。只有需要代理或自动捕获时才需要。 |
|
|
41
|
+
|
|
42
|
+
### 开发期行为开关
|
|
43
|
+
|
|
44
|
+
| 配置 | 类型 | 默认值 | 作用 |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| `enabled` | `boolean` | `true` | 总开关。为 `false` 时插件直接放行请求,不读取 mock、不代理、不捕获。 |
|
|
47
|
+
| `proxy` | `boolean` | `true` | mock 未命中时是否代理到 `proxyBaseUrl`。需要同时配置 `proxyBaseUrl`。 |
|
|
48
|
+
| `capture` | `boolean` | `true` | 代理 JSON 响应是否保存为本地 mock。需要 `proxy=true` 且配置 `proxyBaseUrl`。 |
|
|
49
|
+
|
|
50
|
+
当前没有独立的“只录制但不命中已有 mock”模式,因为 `enabled=false` 是总开关。如果需要这个能力,建议后续单独设计一个比 `enabled` 更明确的选项。
|
|
51
|
+
|
|
52
|
+
### 可视化面板
|
|
53
|
+
|
|
54
|
+
| 配置 | 类型 | 默认值 | 作用 |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| `inspector` | `boolean \| InspectorOptions` | `false` | 是否启用可视化 mock 管理面板。 |
|
|
57
|
+
| `inspector.route` | `string` | `'/__mock/'` | 面板访问路径。 |
|
|
58
|
+
| `inspector.enableToggle` | `boolean` | `true` | 是否允许在面板里切换 mock 文件的 `enable` 字段。 |
|
|
59
|
+
|
|
60
|
+
示例:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
automock({
|
|
64
|
+
proxyBaseUrl: 'https://api.example.com',
|
|
65
|
+
inspector: {
|
|
66
|
+
route: '/__mock/',
|
|
67
|
+
enableToggle: true,
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 生产构建 mock
|
|
73
|
+
|
|
74
|
+
生产构建 mock 由两个部分组成:
|
|
75
|
+
|
|
76
|
+
1. Vite 插件在构建时生成 `mock-data.json`。
|
|
77
|
+
2. 浏览器端通过 `vite-plugin-automock/client` 初始化 Axios 拦截器。
|
|
78
|
+
|
|
79
|
+
| 配置 | 类型 | 默认值 | 作用 |
|
|
80
|
+
| --- | --- | --- | --- |
|
|
81
|
+
| `productionMock` | `boolean \| 'auto'` | `false` | 是否给客户端拦截器注入启用状态,并允许构建 mock bundle。 |
|
|
82
|
+
| `bundleMockData` | `boolean` | `true` | `productionMock` 启用时,是否生成 mock 数据包。 |
|
|
83
|
+
| `bundleOutputPath` | `string` | `'public/mock-data.json'` | mock 数据包输出路径。相对路径基于当前工作目录,也支持绝对路径。 |
|
|
84
|
+
|
|
85
|
+
`productionMock` 的语义:
|
|
86
|
+
|
|
87
|
+
| 值 | 行为 |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `false` 或不配置 | 不启用客户端 mock,不生成 `mock-data.json`。 |
|
|
90
|
+
| `true` | 显式启用客户端 mock,并在构建时生成 `mock-data.json`。 |
|
|
91
|
+
| `'auto'` | 在非 `production` mode 启用,在 `production` mode 关闭。适合 `vite build --mode staging` 这类内部环境。 |
|
|
92
|
+
|
|
93
|
+
生产 mock 示例:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// vite.config.ts
|
|
97
|
+
automock({
|
|
98
|
+
mockDir: 'mock',
|
|
99
|
+
productionMock: true,
|
|
100
|
+
bundleMockData: true,
|
|
101
|
+
bundleOutputPath: 'public/mock-data.json',
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// src/main.ts
|
|
107
|
+
import axios from 'axios'
|
|
108
|
+
import { initMockInterceptor } from 'vite-plugin-automock/client'
|
|
109
|
+
|
|
110
|
+
await initMockInterceptor(axios)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
注意:动态 mock 例如 `data: () => ({ ... })` 可以在开发期 middleware 中运行,但不会进入生产 `mock-data.json`,因为函数无法安全序列化。
|
|
114
|
+
|
|
115
|
+
## 常见场景
|
|
116
|
+
|
|
117
|
+
### 默认开发模式:mock 优先,未命中就代理并捕获
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
automock({
|
|
121
|
+
proxyBaseUrl: 'https://api.example.com',
|
|
122
|
+
pathRewrite: (path) => path.replace(/^\/api/, ''),
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 离线 mock 模式:只用本地 mock,不访问后端
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
automock({
|
|
130
|
+
mockDir: 'mock',
|
|
131
|
+
proxy: false,
|
|
132
|
+
capture: false,
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
如果请求没有命中 mock,会交给后续 Vite middleware 处理。
|
|
137
|
+
|
|
138
|
+
### 只使用 Vite 自己的代理
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
automock({
|
|
142
|
+
enabled: false,
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
然后在 Vite `server.proxy` 里配置真实代理。这个模式不会读取本地 mock,也不会自动捕获后端响应。
|
|
147
|
+
|
|
148
|
+
### 带面板的开发模式
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
automock({
|
|
152
|
+
proxyBaseUrl: 'https://api.example.com',
|
|
153
|
+
inspector: true,
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
启动 Vite 后访问 `/__mock/`。
|
|
158
|
+
|
|
159
|
+
### 内部预发环境启用 mock,正式生产关闭
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
automock({
|
|
163
|
+
mockDir: 'mock',
|
|
164
|
+
productionMock: 'auto',
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`vite build --mode staging` 会启用,`vite build --mode production` 会关闭。
|
|
169
|
+
|
|
170
|
+
## 配置之间的关系
|
|
171
|
+
|
|
172
|
+
- `enabled=false` 优先级最高,会跳过插件全部处理。
|
|
173
|
+
- `proxy=false` 只影响未命中 mock 后是否代理,不影响已启用 mock 的返回。
|
|
174
|
+
- `capture=false` 只影响代理响应是否保存,不影响代理本身。
|
|
175
|
+
- `capture=true` 但没有 `proxyBaseUrl` 时不会捕获,因为没有插件代理请求。
|
|
176
|
+
- `productionMock` 只影响客户端拦截器和构建期 bundle,不影响开发服务器 middleware 是否返回 mock。
|
|
177
|
+
- `bundleMockData=true` 只有在 `productionMock` 解析为启用时才会生效。
|
|
178
|
+
- `inspector` 只在开发服务器中可用,不参与生产客户端拦截。
|
|
179
|
+
|
|
180
|
+
## 可评估的精简候选
|
|
181
|
+
|
|
182
|
+
这些不是本次变更要删除的功能,只是为了后续决策时更容易看清复杂度来源:
|
|
183
|
+
|
|
184
|
+
1. `enabled`、`proxy`、`capture` 三个开关容易被理解成完全独立,但 `enabled` 实际是总开关。
|
|
185
|
+
2. `productionMock`、`bundleMockData`、客户端 Axios 拦截器是一整套生产 mock 能力,如果项目只需要开发期 mock,可以考虑弱化或拆出文档。
|
|
186
|
+
3. `initMockInterceptorForPureHttp` 是面向特定封装的适配层,可以评估是否还需要作为核心 API 暴露。
|
|
187
|
+
4. 二进制 mock 对下载、图片、文档接口有价值,但会增加 mock 文件管理复杂度。
|
|
188
|
+
5. 可视化 inspector 很方便,但配置、模板和 API 较多,可以考虑作为可选高级能力呈现。
|
|
189
|
+
|
|
190
|
+
## 排查清单
|
|
191
|
+
|
|
192
|
+
- 请求没有命中 mock:检查 URL 的 pathname 是否在 `apiPrefix` 下,mock 文件是否位于 `mockDir/<path>/<method>.js`。
|
|
193
|
+
- 请求被错误代理:确认没有同时配置同前缀的 Vite `server.proxy` 和 automock `proxyBaseUrl`。
|
|
194
|
+
- 没有生成 `mock-data.json`:确认 `productionMock` 不是 `false`,且 `bundleMockData=true`。
|
|
195
|
+
- 生产拦截器没有生效:确认应用入口调用了 `initMockInterceptor(axios)`,并且浏览器能访问 `/mock-data.json`。
|
|
196
|
+
- 动态 mock 没有进入生产 bundle:这是预期行为,动态函数只适合开发期 middleware。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-automock",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
|
-
"README.md"
|
|
22
|
+
"README.md",
|
|
23
|
+
"docs"
|
|
23
24
|
],
|
|
24
25
|
"scripts": {
|
|
25
26
|
"build": "tsup",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/interceptor.ts"],"sourcesContent":["import type {\n AxiosInstance,\n AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\n\ndeclare const __AUTOMOCK_ENABLED__: boolean;\n\nexport type MockBundleData = {\n enable: boolean;\n data: unknown;\n delay?: number;\n status?: number;\n isBinary?: boolean;\n};\n\nexport type MockInterceptorOptions = {\n mockData: Record<string, MockBundleData>;\n enabled?: boolean | (() => boolean);\n onMockHit?: (url: string, method: string, mock: MockBundleData) => void;\n onBypass?: (url: string, method: string, reason: string) => void;\n};\n\ntype MockEnabledState = boolean | undefined;\n\ndeclare global {\n interface Window {\n __MOCK_ENABLED__?: MockEnabledState;\n }\n}\n\nclass MockInterceptor {\n private options: MockInterceptorOptions;\n\n constructor(options: MockInterceptorOptions) {\n this.options = options;\n }\n\n /**\n * Check if mock is enabled\n */\n isEnabled(): boolean {\n // Check runtime flag first\n if (window.__MOCK_ENABLED__ !== undefined) {\n return window.__MOCK_ENABLED__;\n }\n // Check environment variable\n if (typeof this.options.enabled === \"function\") {\n return this.options.enabled();\n }\n return this.options.enabled ?? false;\n }\n\n /**\n * Find matching mock data for the request\n * Supports both formats:\n * - \"GET /api/v1/asset/xxx\" (HTTP method + URL)\n * - \"/api/v1/asset/xxx/get.js\" (File path format from automock plugin)\n */\n private findMock(url: string, method: string): MockBundleData | null {\n const methodUpper = method.toUpperCase();\n const methodLower = method.toLowerCase();\n\n // Try HTTP method + URL format first (for compatibility)\n const httpMethodKey = `${methodUpper} ${url}`;\n if (this.options.mockData[httpMethodKey]) {\n return this.options.mockData[httpMethodKey];\n }\n\n // Try file path format from automock plugin: /api/v1/asset/xxx/get.js\n const filePathKey = `${url}/${methodLower}.js`;\n if (this.options.mockData[filePathKey]) {\n return this.options.mockData[filePathKey];\n }\n\n return null;\n }\n\n /**\n * Determine if request should be mocked\n */\n shouldMock(url: string, method: string): boolean {\n if (!this.isEnabled()) {\n return false;\n }\n\n const mock = this.findMock(url, method);\n return mock !== null && mock.enable;\n }\n\n /**\n * Get mock data for request\n */\n getMock(url: string, method: string): MockBundleData | null {\n if (!this.shouldMock(url, method)) {\n return null;\n }\n return this.findMock(url, method);\n }\n\n /**\n * Setup axios interceptor\n */\n setupAxios(axiosInstance: AxiosInstance): void {\n axiosInstance.interceptors.request.use(\n async (config: InternalAxiosRequestConfig) => {\n const url = config.url || \"\";\n const method = config.method?.toUpperCase() || \"GET\";\n\n const mock = this.getMock(url, method);\n\n if (mock) {\n // Trigger mock hit callback\n this.options.onMockHit?.(url, method, mock);\n\n // Set adapter to return mock data\n (config as InternalAxiosRequestConfig & { adapter: () => Promise<AxiosResponse> }).adapter = async () => {\n const { data, delay = 0, status = 200 } = mock;\n\n // Simulate delay\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n return {\n data,\n status,\n statusText: \"OK\",\n headers: {},\n config,\n };\n };\n } else {\n // Trigger bypass callback\n const reason = !this.isEnabled()\n ? \"Mock disabled globally\"\n : \"No matching mock found or mock disabled\";\n this.options.onBypass?.(url, method, reason);\n }\n\n return config;\n },\n (error: unknown) => Promise.reject(error),\n );\n }\n}\n\n/**\n * Create mock interceptor instance\n */\nexport function createMockInterceptor(\n options: MockInterceptorOptions,\n): MockInterceptor {\n return new MockInterceptor(options);\n}\n\n/**\n * Load mock data from bundled JSON file\n */\nexport async function loadMockData(): Promise<Record<string, MockBundleData>> {\n try {\n // In production, mock-data.json is generated by automock plugin\n // Use absolute path to load it from dist directory\n const response = await fetch(\"/mock-data.json\");\n if (!response.ok) {\n console.warn(\n \"[MockInterceptor] Failed to load mock-data.json:\",\n response.statusText,\n );\n return {};\n }\n const data = (await response.json()) as Record<string, MockBundleData>;\n return data;\n } catch (error) {\n console.warn(\"[MockInterceptor] Failed to load mock-data.json:\", error);\n return {};\n }\n}\n\nasync function createInterceptorFromBundle(): Promise<MockInterceptor> {\n const mockData = await loadMockData();\n const isEnabled = typeof __AUTOMOCK_ENABLED__ !== \"undefined\" && __AUTOMOCK_ENABLED__;\n\n return new MockInterceptor({\n mockData,\n enabled: isEnabled,\n onMockHit: (url: string, method: string) => {\n console.log(`[MOCK HIT] ${method} ${url}`);\n },\n onBypass: (url: string, method: string, reason: string) => {\n console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);\n },\n });\n}\n\nexport async function initMockInterceptor(\n axiosInstance?: AxiosInstance,\n): Promise<void> {\n if (!axiosInstance) {\n throw new Error(\n \"[MockInterceptor] axiosInstance is required. Please provide an axios instance.\",\n );\n }\n\n const interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(axiosInstance);\n}\n\nexport function setMockEnabled(enabled: boolean): void {\n window.__MOCK_ENABLED__ = enabled;\n}\n\nexport function isMockEnabled(): boolean {\n return !!window.__MOCK_ENABLED__;\n}\n\ntype HttpInstance = {\n constructor: { axiosInstance: AxiosInstance };\n};\n\nlet httpInstanceRef: HttpInstance | undefined;\n\nexport function registerHttpInstance(http: HttpInstance): void {\n httpInstanceRef = http;\n}\n\nexport async function initMockInterceptorForPureHttp(): Promise<void> {\n if (!httpInstanceRef) {\n throw new Error(\n \"[MockInterceptor] http instance not registered. Call registerHttpInstance(http) first.\",\n );\n }\n\n const interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);\n}\n"],"mappings":"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","names":[]}
|