- 新增NextAuth认证系统,支持Google和GitHub登录 - 添加登录模态框组件和用户头像显示 - 重构音频播放器,支持倍速控制和状态同步 - 优化播客卡片显示当前播放状态和生成状态 - 新增API调用追踪工具和防重复调用Hook - 修复多个API重复调用问题并添加详细文档 - 改进音频文件处理流程,支持MP3格式输出 - 更新类型定义和组件Props以支持新功能
5.2 KiB
5.2 KiB
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合并为一个:
// 修复前:两个独立的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:
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组件中:
// 修复前:多个依赖项可能导致重复调用
useEffect(() => {
fetchVoices();
}, [selectedConfig, selectedConfigName]);
// 修复后:只依赖必要的状态
useEffect(() => {
if (!selectedConfigName) {
setVoices([]);
return;
}
fetchVoices();
}, [selectedConfigName]); // 只依赖配置名称
4. 添加API调用追踪器
创建了 src/utils/apiCallTracker.ts 用于开发环境下监控API调用:
// 自动检测重复调用
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. 开发环境调试
打开浏览器开发者工具,在控制台中可以使用:
// 查看API调用统计
window.apiDebug.showStats();
// 清空统计数据
window.apiDebug.clearStats();
2. 网络面板监控
在浏览器开发者工具的Network面板中:
- 刷新页面,观察
/api/podcast-status只被调用一次 - 切换TTS配置,观察
/api/tts-voices不会重复调用
3. 控制台日志
开发环境下会自动输出API调用日志:
📡 API调用:- 正常调用🚨 检测到重复API调用:- 重复调用警告
最佳实践建议
- useEffect合并原则:相关的副作用应该在同一个useEffect中处理
- 依赖项最小化:只包含真正需要的依赖项
- 防重复调用:对于可能重复的API调用使用防重复机制
- 开发调试工具:在开发环境中添加监控和调试工具
- 错误处理:确保API调用失败时不会影响后续调用
相关文件
src/app/page.tsx- 主页组件修复src/components/PodcastCreator.tsx- 播客创建器组件修复src/components/ConfigSelector.tsx- 配置选择器组件修复src/hooks/useApiCall.ts- 防重复调用Hook(新增)src/utils/apiCallTracker.ts- API调用追踪器(新增)
注意事项
- 修复后的代码保持了原有功能不变
- 所有修改都向后兼容
- 调试工具只在开发环境中启用,不会影响生产环境性能
- 建议在部署前进行充分测试,确保所有功能正常工作