系统性的问题诊断和解决方法,帮助您快速定位和解决 @feoe/fs-router 相关问题。
首先确定问题类型:
1# 收集环境信息
2node --version
3npm --version
4# 或
5yarn --version
6
7# 查看项目依赖
8npm list @feoe/fs-router
9npm list react-router-dom
10npm list typescript
11
12# 检查构建工具版本
13npm list vite
14# 或
15npm list webpack
1// vite.config.ts
2export default defineConfig({
3 plugins: [
4 FileBasedRouterVite({
5 routesDirectory: 'src/routes',
6 generatedRoutesPath: 'src/routes.tsx',
7
8 // 启用调试
9 development: {
10 logGeneration: true,
11 showDebugInfo: true,
12 validateRoutes: true
13 }
14 })
15 ]
16})
症状: src/routes.tsx
文件不存在或内容为空
诊断步骤:
检查插件配置
1// 确认插件已正确添加到配置中
2console.log('Vite 插件:', config.plugins)
检查路由目录
1# 确认目录存在且包含路由文件
2ls -la src/routes/
3find src/routes -name "*.tsx" -o -name "*.ts"
检查文件权限
1# 确认有写入权限
2touch src/routes.tsx
3ls -la src/routes.tsx
查看构建日志
1# 启动开发服务器并查看日志
2npm run dev --verbose
解决方案:
1// 确保配置正确
2FileBasedRouterVite({
3 routesDirectory: 'src/routes', // 确保路径正确
4 generatedRoutesPath: 'src/routes.tsx', // 确保父目录存在
5 enableGeneration: true, // 确保启用生成
6 watch: true // 确保启用监听
7})
症状: 编译时出现类型相关错误
诊断步骤:
检查 TypeScript 配置
1// tsconfig.json
2{
3 "compilerOptions": {
4 "moduleResolution": "node",
5 "esModuleInterop": true,
6 "allowSyntheticDefaultImports": true,
7 "jsx": "react-jsx"
8 },
9 "include": [
10 "src/**/*",
11 "src/routes.tsx" // 确保包含生成的文件
12 ]
13}
检查类型生成
1# 确认类型文件是否生成
2ls -la src/routes-type.ts
验证导入路径
1// 检查导入是否正确
2import { routes } from '@/routes' // 使用别名
3// 或
4import { routes } from './routes' // 使用相对路径
解决方案:
1// 启用类型生成
2FileBasedRouterVite({
3 enableGeneration: true,
4 typeGenerateOptions: {
5 routesTypeFile: 'src/routes-type.ts',
6 generateRouteParams: true,
7 generateLoaderTypes: true,
8 routesDirectories: []
9 }
10})
症状: 访问路由时显示 404 或错误页面
诊断步骤:
检查生成的路由配置
1// 在控制台查看生成的路由
2import { routes } from '@/routes'
3console.log('生成的路由:', JSON.stringify(routes, null, 2))
验证文件结构
1# 检查文件命名和结构
2tree src/routes/
检查路由器配置
1// 确认路由器配置正确
2const router = createBrowserRouter(routes)
3// 或
4const router = createHashRouter(routes)
解决方案:
1// 确保文件命名正确
2src/routes/
3├── page.tsx # 对应 /
4├── about/
5│ └── page.tsx # 对应 /about
6└── user/
7 └── [id]/
8 └── page.tsx # 对应 /user/:id
9
10// 确保组件正确导出
11export default function HomePage() {
12 return <div>首页</div>
13}
症状: useParams()
返回 undefined 或错误的值
诊断步骤:
检查文件命名
1# 确认使用方括号命名
2ls -la src/routes/user/[id]/
检查参数获取
1// 调试参数获取
2const params = useParams()
3console.log('路由参数:', params)
验证路由匹配
1// 检查当前路由
2const location = useLocation()
3console.log('当前路径:', location.pathname)
解决方案:
1// 正确的文件命名和参数获取
2// 文件: src/routes/user/[id]/page.tsx
3import { useParams } from 'react-router-dom'
4
5export default function UserPage() {
6 const { id } = useParams() // 参数名对应文件名
7
8 if (!id) {
9 return <div>用户 ID 不存在</div>
10 }
11
12 return <div>用户 ID: {id}</div>
13}
安装扩展
检查路由状态
1// 在组件中添加调试信息
2const location = useLocation()
3const params = useParams()
4const matches = useMatches()
5
6console.log('路由调试信息:', {
7 location,
8 params,
9 matches
10})
1// 监控 loader 请求
2export async function loader({ params, request }) {
3 console.log('Loader 调用:', {
4 params,
5 url: request.url,
6 method: request.method
7 })
8
9 try {
10 const data = await fetchData(params.id)
11 console.log('Loader 数据:', data)
12 return data
13 } catch (error) {
14 console.error('Loader 错误:', error)
15 throw error
16 }
17}
1// 测量组件渲染时间
2import { Profiler } from 'react'
3
4function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
5 console.log('组件性能:', {
6 id,
7 phase,
8 actualDuration,
9 baseDuration
10 })
11
12 if (actualDuration > 16) {
13 console.warn(`组件 ${id} 渲染时间过长: ${actualDuration}ms`)
14 }
15}
16
17export default function App() {
18 return (
19 <Profiler id="App" onRender={onRenderCallback}>
20 <RouterProvider router={router} />
21 </Profiler>
22 )
23}
1// 全局错误边界
2class GlobalErrorBoundary extends Component {
3 constructor(props) {
4 super(props)
5 this.state = { hasError: false, error: null }
6 }
7
8 static getDerivedStateFromError(error) {
9 return { hasError: true, error }
10 }
11
12 componentDidCatch(error, errorInfo) {
13 console.error('全局错误:', error, errorInfo)
14
15 // 发送错误报告
16 this.reportError(error, errorInfo)
17 }
18
19 reportError(error, errorInfo) {
20 // 发送到错误监控服务
21 if (typeof window !== 'undefined' && window.gtag) {
22 window.gtag('event', 'exception', {
23 description: error.toString(),
24 fatal: false
25 })
26 }
27 }
28
29 render() {
30 if (this.state.hasError) {
31 return (
32 <div className="error-page">
33 <h1>应用出现错误</h1>
34 <details>
35 <summary>错误详情</summary>
36 <pre>{this.state.error?.toString()}</pre>
37 </details>
38 </div>
39 )
40 }
41
42 return this.props.children
43 }
44}
1// 日志工具
2class Logger {
3 static debug(message: string, data?: any) {
4 if (process.env.NODE_ENV === 'development') {
5 console.log(`[DEBUG] ${message}`, data)
6 }
7 }
8
9 static info(message: string, data?: any) {
10 console.info(`[INFO] ${message}`, data)
11 }
12
13 static warn(message: string, data?: any) {
14 console.warn(`[WARN] ${message}`, data)
15 }
16
17 static error(message: string, error?: Error, data?: any) {
18 console.error(`[ERROR] ${message}`, error, data)
19
20 // 发送到错误监控服务
21 this.reportError(message, error, data)
22 }
23
24 private static reportError(message: string, error?: Error, data?: any) {
25 // 实现错误报告逻辑
26 }
27}
28
29// 在路由中使用
30export async function loader({ params }) {
31 Logger.debug('加载用户数据', { userId: params.id })
32
33 try {
34 const user = await fetchUser(params.id)
35 Logger.info('用户数据加载成功', { userId: params.id })
36 return { user }
37 } catch (error) {
38 Logger.error('用户数据加载失败', error, { userId: params.id })
39 throw error
40 }
41}
当需要寻求帮助时,请提供以下信息:
1## 问题描述
2[简要描述遇到的问题]
3
4## 环境信息
5- Node.js 版本:
6- 包管理器: npm/yarn/pnpm
7- @feoe/fs-router 版本:
8- React 版本:
9- React Router 版本:
10- 构建工具: Vite/Webpack/Rspack
11- 操作系统:
12
13## 重现步骤
141.
152.
163.
17
18## 期望行为
19[描述期望的正确行为]
20
21## 实际行为
22[描述实际发生的行为]
23
24## 配置文件
25```typescript
26// vite.config.ts 或 webpack.config.js
[粘贴完整的错误信息和堆栈跟踪]
[提供最小的可复现示例代码]
## 预防措施
### 代码审查清单
- [ ] 文件命名符合约定
- [ ] 组件正确导出
- [ ] 路由配置正确
- [ ] TypeScript 类型正确
- [ ] 错误处理完善
- [ ] 性能考虑充分
### 自动化测试
```tsx
// 路由测试示例
import { render, screen } from '@testing-library/react'
import { createMemoryRouter, RouterProvider } from 'react-router-dom'
import { routes } from '@/routes'
describe('路由测试', () => {
test('首页路由正常工作', () => {
const router = createMemoryRouter(routes, {
initialEntries: ['/']
})
render(<RouterProvider router={router} />)
expect(screen.getByText('首页')).toBeInTheDocument()
})
test('动态路由参数正确传递', () => {
const router = createMemoryRouter(routes, {
initialEntries: ['/user/123']
})
render(<RouterProvider router={router} />)
expect(screen.getByText('用户 ID: 123')).toBeInTheDocument()
})
})