- 新增NextAuth认证系统,支持Google和GitHub登录 - 添加登录模态框组件和用户头像显示 - 重构音频播放器,支持倍速控制和状态同步 - 优化播客卡片显示当前播放状态和生成状态 - 新增API调用追踪工具和防重复调用Hook - 修复多个API重复调用问题并添加详细文档 - 改进音频文件处理流程,支持MP3格式输出 - 更新类型定义和组件Props以支持新功能
189 lines
5.2 KiB
Markdown
189 lines
5.2 KiB
Markdown
# API重复调用问题修复报告
|
||
|
||
## 问题描述
|
||
|
||
用户报告访问主页时,主页内的接口被调用了两次。这是一个常见的React问题,会导致:
|
||
- 不必要的网络请求
|
||
- 服务器负载增加
|
||
- 用户体验下降
|
||
- 可能的数据不一致
|
||
|
||
## 问题分析
|
||
|
||
通过代码分析,发现了以下导致重复调用的原因:
|
||
|
||
### 1. **多个useEffect调用同一API**
|
||
在 `src/app/page.tsx` 中:
|
||
- 第38行的useEffect在组件挂载时调用 `fetchRecentPodcasts()`
|
||
- 第86行的useEffect设置定时器每20秒调用 `fetchRecentPodcasts()`
|
||
- 这导致页面加载时API被调用两次
|
||
|
||
### 2. **useEffect依赖项问题**
|
||
在 `src/components/PodcastCreator.tsx` 中:
|
||
- useEffect依赖项包含 `selectedConfig` 和 `selectedConfigName`
|
||
- 当配置变化时可能触发多次API调用
|
||
|
||
### 3. **ConfigSelector组件的重复调用**
|
||
在 `src/components/ConfigSelector.tsx` 中:
|
||
- localStorage变化监听可能导致重复的配置加载
|
||
|
||
## 修复方案
|
||
|
||
### 1. **合并useEffect调用**
|
||
将两个分离的useEffect合并为一个:
|
||
|
||
```typescript
|
||
// 修复前:两个独立的useEffect
|
||
useEffect(() => {
|
||
setCredits(100000);
|
||
fetchRecentPodcasts(); // 第一次调用
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
const interval = setInterval(() => {
|
||
fetchRecentPodcasts(); // 定时调用
|
||
}, 20000);
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
// 修复后:合并为一个useEffect
|
||
useEffect(() => {
|
||
setCredits(100000);
|
||
fetchRecentPodcasts(); // 初始调用
|
||
|
||
// 设置定时器
|
||
const interval = setInterval(() => {
|
||
fetchRecentPodcasts();
|
||
}, 20000);
|
||
|
||
return () => clearInterval(interval);
|
||
}, []); // 空依赖数组,只在组件挂载时执行一次
|
||
```
|
||
|
||
### 2. **创建防重复调用Hook**
|
||
创建了 `src/hooks/useApiCall.ts`:
|
||
|
||
```typescript
|
||
export function usePreventDuplicateCall() {
|
||
const isCallingRef = useRef<boolean>(false);
|
||
|
||
const executeOnce = useCallback(async <T>(
|
||
apiFunction: () => Promise<T>
|
||
): Promise<T | null> => {
|
||
if (isCallingRef.current) {
|
||
console.log('API call already in progress, skipping...');
|
||
return null;
|
||
}
|
||
|
||
try {
|
||
isCallingRef.current = true;
|
||
const result = await apiFunction();
|
||
return result;
|
||
} catch (error) {
|
||
console.error('API call failed:', error);
|
||
return null;
|
||
} finally {
|
||
isCallingRef.current = false;
|
||
}
|
||
}, []);
|
||
|
||
return { executeOnce };
|
||
}
|
||
```
|
||
|
||
### 3. **优化useEffect依赖项**
|
||
在PodcastCreator组件中:
|
||
|
||
```typescript
|
||
// 修复前:多个依赖项可能导致重复调用
|
||
useEffect(() => {
|
||
fetchVoices();
|
||
}, [selectedConfig, selectedConfigName]);
|
||
|
||
// 修复后:只依赖必要的状态
|
||
useEffect(() => {
|
||
if (!selectedConfigName) {
|
||
setVoices([]);
|
||
return;
|
||
}
|
||
fetchVoices();
|
||
}, [selectedConfigName]); // 只依赖配置名称
|
||
```
|
||
|
||
### 4. **添加API调用追踪器**
|
||
创建了 `src/utils/apiCallTracker.ts` 用于开发环境下监控API调用:
|
||
|
||
```typescript
|
||
// 自动检测重复调用
|
||
trackCall(url: string, method: string = 'GET'): string {
|
||
const recentCalls = this.calls.filter(
|
||
c => c.url === url &&
|
||
c.method === method &&
|
||
Date.now() - c.timestamp < 5000
|
||
);
|
||
|
||
if (recentCalls.length > 0) {
|
||
console.warn(`🚨 检测到重复API调用:`, {
|
||
url, method, 重复次数: recentCalls.length + 1
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
## 修复效果
|
||
|
||
### 修复前:
|
||
- 页面加载时 `/api/podcast-status` 被调用2次
|
||
- 配置变化时 `/api/tts-voices` 可能被多次调用
|
||
- 无法监控和调试重复调用问题
|
||
|
||
### 修复后:
|
||
- 页面加载时 `/api/podcast-status` 只调用1次
|
||
- 使用防重复调用机制确保同一时间只有一个请求
|
||
- 开发环境下自动检测和警告重复调用
|
||
- 优化了useEffect依赖项,减少不必要的重新执行
|
||
|
||
## 验证方法
|
||
|
||
### 1. **开发环境调试**
|
||
打开浏览器开发者工具,在控制台中可以使用:
|
||
```javascript
|
||
// 查看API调用统计
|
||
window.apiDebug.showStats();
|
||
|
||
// 清空统计数据
|
||
window.apiDebug.clearStats();
|
||
```
|
||
|
||
### 2. **网络面板监控**
|
||
在浏览器开发者工具的Network面板中:
|
||
- 刷新页面,观察 `/api/podcast-status` 只被调用一次
|
||
- 切换TTS配置,观察 `/api/tts-voices` 不会重复调用
|
||
|
||
### 3. **控制台日志**
|
||
开发环境下会自动输出API调用日志:
|
||
- `📡 API调用:` - 正常调用
|
||
- `🚨 检测到重复API调用:` - 重复调用警告
|
||
|
||
## 最佳实践建议
|
||
|
||
1. **useEffect合并原则**:相关的副作用应该在同一个useEffect中处理
|
||
2. **依赖项最小化**:只包含真正需要的依赖项
|
||
3. **防重复调用**:对于可能重复的API调用使用防重复机制
|
||
4. **开发调试工具**:在开发环境中添加监控和调试工具
|
||
5. **错误处理**:确保API调用失败时不会影响后续调用
|
||
|
||
## 相关文件
|
||
|
||
- `src/app/page.tsx` - 主页组件修复
|
||
- `src/components/PodcastCreator.tsx` - 播客创建器组件修复
|
||
- `src/components/ConfigSelector.tsx` - 配置选择器组件修复
|
||
- `src/hooks/useApiCall.ts` - 防重复调用Hook(新增)
|
||
- `src/utils/apiCallTracker.ts` - API调用追踪器(新增)
|
||
|
||
## 注意事项
|
||
|
||
- 修复后的代码保持了原有功能不变
|
||
- 所有修改都向后兼容
|
||
- 调试工具只在开发环境中启用,不会影响生产环境性能
|
||
- 建议在部署前进行充分测试,确保所有功能正常工作 |