docs: 将所有文档迁移到 docs/ 目录
- 移动42个文档文件到 docs/ 目录 - 更新 .gitignore 允许 docs/ 下的 .md 文件 - 删除根目录下的重复文档文件 📁 文档分类: - StockDetailPanel 重构文档(3个) - PostHog 集成文档(6个) - 系统架构和API文档(33个) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
364
docs/ERROR_FIX_REPORT.md
Normal file
364
docs/ERROR_FIX_REPORT.md
Normal file
@@ -0,0 +1,364 @@
|
||||
# 黑屏问题修复报告
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
**现象**: 注册页面点击"获取二维码"按钮,API 请求失败时页面变成黑屏
|
||||
|
||||
**根本原因**:
|
||||
1. **缺少全局 ErrorBoundary** - 组件错误未被捕获,导致整个 React 应用崩溃
|
||||
2. **缺少 Promise rejection 处理** - 异步错误(AxiosError)未被捕获
|
||||
3. **ErrorBoundary 组件未正确导出** - 虽然组件存在但无法使用
|
||||
4. **错误提示被注释** - 用户无法看到具体错误信息
|
||||
|
||||
---
|
||||
|
||||
## ✅ 已实施的修复方案
|
||||
|
||||
### 1. 修复 ErrorBoundary 导出 ✓
|
||||
|
||||
**文件**: `src/components/ErrorBoundary.js`
|
||||
|
||||
**问题**: 文件末尾只有 `export` 没有完整导出语句
|
||||
|
||||
**修复**:
|
||||
```javascript
|
||||
// ❌ 修复前
|
||||
export
|
||||
|
||||
// ✅ 修复后
|
||||
export default ErrorBoundary;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 在 App.js 添加全局 ErrorBoundary ✓
|
||||
|
||||
**文件**: `src/App.js`
|
||||
|
||||
**修复**: 在最外层添加 ErrorBoundary 包裹
|
||||
|
||||
```javascript
|
||||
export default function App() {
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
<ErrorBoundary> {/* ✅ 添加全局错误边界 */}
|
||||
<AuthProvider>
|
||||
<AppContent />
|
||||
</AuthProvider>
|
||||
</ErrorBoundary>
|
||||
</ChakraProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**效果**: 捕获所有 React 组件渲染错误,防止整个应用崩溃
|
||||
|
||||
---
|
||||
|
||||
### 3. 添加全局 Promise Rejection 处理 ✓
|
||||
|
||||
**文件**: `src/App.js`
|
||||
|
||||
**问题**: ErrorBoundary 只能捕获同步错误,无法捕获异步 Promise rejection
|
||||
|
||||
**修复**: 添加全局事件监听器
|
||||
|
||||
```javascript
|
||||
export default function App() {
|
||||
// 全局错误处理:捕获未处理的 Promise rejection
|
||||
useEffect(() => {
|
||||
const handleUnhandledRejection = (event) => {
|
||||
console.error('未捕获的 Promise rejection:', event.reason);
|
||||
event.preventDefault(); // 阻止默认处理,防止崩溃
|
||||
};
|
||||
|
||||
const handleError = (event) => {
|
||||
console.error('全局错误:', event.error);
|
||||
event.preventDefault(); // 阻止默认处理,防止崩溃
|
||||
};
|
||||
|
||||
window.addEventListener('unhandledrejection', handleUnhandledRejection);
|
||||
window.addEventListener('error', handleError);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('unhandledrejection', handleUnhandledRejection);
|
||||
window.removeEventListener('error', handleError);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 捕获所有未处理的 Promise rejection(如 AxiosError)
|
||||
- 记录错误到控制台便于调试
|
||||
- 阻止应用崩溃和黑屏
|
||||
|
||||
---
|
||||
|
||||
### 4. 在 Auth Layout 添加 ErrorBoundary ✓
|
||||
|
||||
**文件**: `src/layouts/Auth.js`
|
||||
|
||||
**修复**: 为认证路由添加独立的错误边界
|
||||
|
||||
```javascript
|
||||
export default function Auth() {
|
||||
return (
|
||||
<ErrorBoundary> {/* ✅ Auth 专属错误边界 */}
|
||||
<Box minH="100vh">
|
||||
<Routes>
|
||||
{/* ... 路由配置 */}
|
||||
</Routes>
|
||||
</Box>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**效果**: 认证页面的错误不会影响整个应用
|
||||
|
||||
---
|
||||
|
||||
### 5. 恢复 WechatRegister 错误提示 ✓
|
||||
|
||||
**文件**: `src/components/Auth/WechatRegister.js`
|
||||
|
||||
**问题**: Toast 错误提示被注释,用户无法看到错误原因
|
||||
|
||||
**修复**:
|
||||
```javascript
|
||||
} catch (error) {
|
||||
console.error('获取微信授权失败:', error);
|
||||
toast({ // ✅ 恢复 Toast 提示
|
||||
title: "获取微信授权失败",
|
||||
description: error.response?.data?.error || error.message || "请稍后重试",
|
||||
status: "error",
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 完整错误保护体系
|
||||
|
||||
现在系统有**四层错误保护**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 第1层: 组件级 try-catch │
|
||||
│ • WechatRegister.getWechatQRCode() │
|
||||
│ • SignIn.openWechatLogin() │
|
||||
│ • 显示 Toast 错误提示 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓ 未捕获的错误
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 第2层: 页面级 ErrorBoundary (Auth.js) │
|
||||
│ • 捕获认证页面的 React 错误 │
|
||||
│ • 显示错误页面 + 重载按钮 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓ 未捕获的错误
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 第3层: 全局 ErrorBoundary (App.js) │
|
||||
│ • 捕获所有 React 组件错误 │
|
||||
│ • 最后的防线,防止白屏 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓ 异步错误
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 第4层: 全局 Promise Rejection 处理 (App.js) │
|
||||
│ • 捕获所有未处理的 Promise rejection │
|
||||
│ • 记录到控制台,阻止应用崩溃 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 修复前 vs 修复后
|
||||
|
||||
| 场景 | 修复前 | 修复后 |
|
||||
|-----|-------|-------|
|
||||
| **API 请求失败** | 黑屏 ❌ | Toast 提示 + 页面正常 ✅ |
|
||||
| **组件渲染错误** | 黑屏 ❌ | 错误页面 + 重载按钮 ✅ |
|
||||
| **Promise rejection** | 黑屏 ❌ | 控制台日志 + 页面正常 ✅ |
|
||||
| **用户体验** | 极差(无法恢复) | 优秀(可继续操作) |
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试场景 1: API 请求失败
|
||||
```
|
||||
操作: 点击"获取二维码",后端返回错误
|
||||
预期:
|
||||
✅ 显示 Toast 错误提示
|
||||
✅ 页面保持正常显示
|
||||
✅ 可以重新点击按钮
|
||||
```
|
||||
|
||||
### 测试场景 2: 网络错误
|
||||
```
|
||||
操作: 断网状态下点击"获取二维码"
|
||||
预期:
|
||||
✅ 显示网络错误提示
|
||||
✅ 页面不黑屏
|
||||
✅ 控制台记录 AxiosError
|
||||
```
|
||||
|
||||
### 测试场景 3: 组件渲染错误
|
||||
```
|
||||
操作: 人为制造组件错误(如访问 undefined 属性)
|
||||
预期:
|
||||
✅ ErrorBoundary 捕获错误
|
||||
✅ 显示错误页面和"重新加载"按钮
|
||||
✅ 点击按钮可恢复
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 调试指南
|
||||
|
||||
### 查看错误日志
|
||||
|
||||
打开浏览器开发者工具 (F12),查看 Console 面板:
|
||||
|
||||
1. **组件级错误**:
|
||||
```
|
||||
❌ 获取微信授权失败: AxiosError {...}
|
||||
```
|
||||
|
||||
2. **Promise rejection**:
|
||||
```
|
||||
❌ 未捕获的 Promise rejection: Error: Network Error
|
||||
```
|
||||
|
||||
3. **全局错误**:
|
||||
```
|
||||
❌ 全局错误: TypeError: Cannot read property 'xxx' of undefined
|
||||
```
|
||||
|
||||
### 检查 ErrorBoundary 是否生效
|
||||
|
||||
1. 在开发模式下,React 会显示错误详情 overlay
|
||||
2. 关闭 overlay 后,应该看到 ErrorBoundary 的错误页面
|
||||
3. 生产模式下直接显示 ErrorBoundary 错误页面
|
||||
|
||||
---
|
||||
|
||||
## 📝 修改文件清单
|
||||
|
||||
| 文件 | 修改内容 | 状态 |
|
||||
|-----|---------|------|
|
||||
| `src/components/ErrorBoundary.js` | 添加 `export default` | ✅ |
|
||||
| `src/App.js` | 添加 ErrorBoundary + Promise rejection 处理 | ✅ |
|
||||
| `src/layouts/Auth.js` | 添加 ErrorBoundary | ✅ |
|
||||
| `src/components/Auth/WechatRegister.js` | 恢复 Toast 错误提示 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 开发环境 vs 生产环境
|
||||
|
||||
**开发环境**:
|
||||
- React 会显示红色错误 overlay
|
||||
- ErrorBoundary 的错误详情会显示
|
||||
- 控制台有完整的错误堆栈
|
||||
|
||||
**生产环境**:
|
||||
- 不显示错误 overlay
|
||||
- 直接显示 ErrorBoundary 的用户友好页面
|
||||
- 控制台仅记录简化的错误信息
|
||||
|
||||
### Promise Rejection 处理
|
||||
|
||||
- `event.preventDefault()` 阻止浏览器默认行为(控制台红色错误)
|
||||
- 但错误仍会被记录到 `console.error`
|
||||
- 应用不会崩溃,用户可继续操作
|
||||
|
||||
---
|
||||
|
||||
## 🎯 后续优化建议
|
||||
|
||||
### 1. 添加错误上报服务(可选)
|
||||
|
||||
集成 Sentry 或其他错误监控服务:
|
||||
|
||||
```javascript
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
||||
// 在 index.js 初始化
|
||||
Sentry.init({
|
||||
dsn: "YOUR_SENTRY_DSN",
|
||||
environment: process.env.NODE_ENV,
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 改进用户体验
|
||||
|
||||
- 为不同类型的错误显示不同的图标和文案
|
||||
- 添加"联系客服"按钮
|
||||
- 提供常见问题解答链接
|
||||
|
||||
### 3. 优化错误恢复
|
||||
|
||||
- 实现细粒度的错误边界(特定功能区域)
|
||||
- 提供局部重试而不是刷新整个页面
|
||||
- 缓存用户输入,错误恢复后自动填充
|
||||
|
||||
---
|
||||
|
||||
## 📈 技术细节
|
||||
|
||||
### ErrorBoundary 原理
|
||||
|
||||
```javascript
|
||||
class ErrorBoundary extends React.Component {
|
||||
componentDidCatch(error, errorInfo) {
|
||||
// 捕获子组件树中的所有错误
|
||||
// 但无法捕获:
|
||||
// 1. 事件处理器中的错误
|
||||
// 2. 异步代码中的错误 (setTimeout, Promise)
|
||||
// 3. ErrorBoundary 自身的错误
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Promise Rejection 处理原理
|
||||
|
||||
```javascript
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
// event.reason 包含 Promise rejection 的原因
|
||||
// event.promise 是被 reject 的 Promise
|
||||
event.preventDefault(); // 阻止默认行为
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
### 修复成果
|
||||
|
||||
✅ **彻底解决黑屏问题**
|
||||
- API 请求失败不再导致崩溃
|
||||
- 用户可以看到清晰的错误提示
|
||||
- 页面可以正常继续使用
|
||||
|
||||
✅ **建立完整错误处理体系**
|
||||
- 4 层错误保护机制
|
||||
- 覆盖同步和异步错误
|
||||
- 开发和生产环境都适用
|
||||
|
||||
✅ **提升用户体验**
|
||||
- 从"黑屏崩溃"到"友好提示"
|
||||
- 提供错误恢复途径
|
||||
- 便于问题排查和调试
|
||||
|
||||
---
|
||||
|
||||
**修复完成时间**: 2025-10-14
|
||||
**修复者**: Claude Code
|
||||
**版本**: 3.0.0
|
||||
|
||||
Reference in New Issue
Block a user