## 问题
控制台警告:
```
MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
11 response:mocked listeners added. Use emitter.setMaxListeners() to increase limit
```
## 根本原因
类似 PostHog 的问题:
1. **React StrictMode 双重渲染** - 开发环境组件渲染两次
2. **热重载** - 代码更改时频繁重新加载模块
3. **缺少启动锁** - `startMockServiceWorker()` 被多次调用
4. **事件监听器累积** - 每次启动添加新 listener,旧的未清理
## 解决方案
### 方案A: 防止重复启动
添加启动状态锁:
```javascript
let isStarting = false;
let isStarted = false;
export async function startMockServiceWorker() {
// 防止重复启动
if (isStarting || isStarted) {
console.log('[MSW] 已启动,跳过重复调用');
return;
}
isStarting = true;
try {
await worker.start({...});
isStarted = true; // 成功后标记
} finally {
isStarting = false; // 无论成功失败都重置
}
}
```
### 方案B: 完善 stop 逻辑
确保正确清理:
```javascript
export function stopMockServiceWorker() {
if (!isStarted) return; // 避免重复停止
worker.stop();
isStarted = false; // 重置状态
console.log('[MSW] Mock Service Worker 已停止');
}
```
## 影响
- ✅ 修复 EventEmitter 内存泄漏警告
- ✅ 防止热重载时重复启动 MSW
- ✅ 正确清理事件监听器
- ✅ 提升开发体验
## 验证
重启开发服务器后:
- ✅ 不再有 MaxListenersExceededWarning
- ✅ MSW 只启动一次
- ✅ 热重载正常工作
- ✅ Mock 功能正常
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
86 lines
2.6 KiB
JavaScript
86 lines
2.6 KiB
JavaScript
// src/mocks/browser.js
|
||
// 浏览器环境的 MSW Worker
|
||
|
||
import { setupWorker } from 'msw/browser';
|
||
import { handlers } from './handlers';
|
||
|
||
// 创建 Service Worker 实例
|
||
export const worker = setupWorker(...handlers);
|
||
|
||
// 启动状态管理(防止重复启动)
|
||
let isStarting = false;
|
||
let isStarted = false;
|
||
|
||
// 启动 Mock Service Worker
|
||
export async function startMockServiceWorker() {
|
||
// 防止重复启动
|
||
if (isStarting || isStarted) {
|
||
console.log('[MSW] Mock Service Worker 已启动或正在启动中,跳过重复调用');
|
||
return;
|
||
}
|
||
|
||
// 只在开发环境且 REACT_APP_ENABLE_MOCK=true 时启动
|
||
const shouldEnableMock = process.env.REACT_APP_ENABLE_MOCK === 'true';
|
||
|
||
if (!shouldEnableMock) {
|
||
console.log('[MSW] Mock 已禁用 (REACT_APP_ENABLE_MOCK=false)');
|
||
return;
|
||
}
|
||
|
||
isStarting = true;
|
||
|
||
try {
|
||
await worker.start({
|
||
// 🎯 智能穿透模式(关键配置)
|
||
// 'bypass': 未定义 Mock 的请求自动转发到真实后端
|
||
// 'warn': 未定义的请求会显示警告(调试用)
|
||
// 'error': 未定义的请求会抛出错误(严格模式)
|
||
onUnhandledRequest: 'bypass',
|
||
|
||
// 自定义 Service Worker URL(如果需要)
|
||
serviceWorker: {
|
||
url: '/mockServiceWorker.js',
|
||
},
|
||
|
||
// 是否在控制台显示启动日志和拦截日志 静默模式(不在控制台打印启动消息)
|
||
quiet: false,
|
||
});
|
||
|
||
isStarted = true;
|
||
console.log(
|
||
'%c[MSW] Mock Service Worker 已启动 🎭',
|
||
'color: #4CAF50; font-weight: bold; font-size: 14px;'
|
||
);
|
||
console.log(
|
||
'%c智能穿透模式:已定义 Mock → 返回假数据 | 未定义 Mock → 转发到 ' + (process.env.REACT_APP_API_URL || '真实后端'),
|
||
'color: #FF9800; font-size: 12px;'
|
||
);
|
||
console.log(
|
||
'%c查看 src/mocks/handlers/ 目录管理 Mock 接口',
|
||
'color: #2196F3; font-size: 12px;'
|
||
);
|
||
} catch (error) {
|
||
console.error('[MSW] 启动失败:', error);
|
||
} finally {
|
||
isStarting = false;
|
||
}
|
||
}
|
||
|
||
// 停止 Mock Service Worker
|
||
export function stopMockServiceWorker() {
|
||
if (!isStarted) {
|
||
console.log('[MSW] Mock Service Worker 未启动,无需停止');
|
||
return;
|
||
}
|
||
|
||
worker.stop();
|
||
isStarted = false;
|
||
console.log('[MSW] Mock Service Worker 已停止');
|
||
}
|
||
|
||
// 重置所有 Handlers
|
||
export function resetMockHandlers() {
|
||
worker.resetHandlers();
|
||
console.log('[MSW] Handlers 已重置');
|
||
}
|