目录结构与文件职责
就做一件事:把项目按文件夹拆解开,搞明白每个目录是干啥的。
- 掌握项目目录结构
- 理解每个文件夹的职责
- 了解 Next.js App Router 约定
- 理解组件分层策略
项目目录树
pygood/
├── app/ # Next.js App Router 页面
│ ├── (marketing)/ # 营销页面组(首页等)
│ ├── (app)/ # 应用页面组(需登录态)
│ │ ├── dashboard/ # 学习仪表盘
│ │ ├── course/[id]/ # 课程学习页(动态路由)
│ │ ├── lab/ # 代码实验室
│ │ ├── profile/ # 个人中心
│ │ └── achievements/ # 成就页面
│ ├── api/ # API Routes(如需后端)
│ ├── layout.tsx # 根布局(html/body/字体)
│ ├── globals.css # 全局样式 + CSS 变量
│ └── page.tsx # 首页
├── components/ # React 组件
│ ├── ui/ # 基础 UI 组件(Button/Card/Badge...)
│ ├── editor/ # 代码编辑器相关
│ ├── course/ # 课程内容组件
│ ├── gamification/ # 游戏化组件(XP/成就/动画)
│ └── layout/ # 布局组件(导航栏/侧边栏)
├── lib/ # 核心逻辑库
│ ├── pyodide/ # Pyodide 执行器封装
│ ├── judge/ # 判题引擎
│ ├── progress/ # 进度管理
│ ├── utils/ # 工具函数
│ └── store/ # Zustand 状态
├── hooks/ # 自定义 React Hooks
├── types/ # TypeScript 类型定义
├── content/ # 课程内容(MDX/JSON)
├── public/ # 静态资源
│ └── pyodide/ # Pyodide WASM 运行时文件
├── tests/ # 测试文件
├── server.js # 生产服务器(静态文件 + API)
├── next.config.js # Next.js 配置
├── tailwind.config.ts # Tailwind 配置
└── package.json # 依赖和脚本app/ 目录详解
Next.js App Router 用文件系统做路由:文件夹名就是 URL 路径,page.tsx 是页面,layout.tsx 是布局。
app/ 目录的路由约定
app/page.tsx → /app/dashboard/page.tsx → /dashboardapp/course/[id]/page.tsx → /course/s1-m1-l1 (动态路由)app/lab/page.tsx → /labapp/profile/page.tsx → /profilelayout.tsx 包裹同级和子级页面:app/layout.tsx → 所有页面共享(字体、主题)app/(app)/layout.tsx → 应用内页面共享(侧边栏)括号文件夹 (marketing) (app) 是路由组,不影响 URL
components/ 目录详解
组件按职责分层,这是大型 React 项目的标准组织方式:
组件分层策略
components/ui/ 原子组件:Button、Card、Badge、Modal 无业务逻辑,纯展示,可复用components/editor/ 编辑器组件:CodeEditor、CodingKeyboard 封装 Monaco Editor 和 Pyodide 交互components/course/ 课程组件:CourseRenderer、ChallengePanel FillBlank、ChoiceQuestion、CodeRunner 处理课程内容的渲染和交互components/gamification/ 游戏化:XpToast、AchievementNotification LevelUpAnimation、StreakCalendarcomponents/layout/ 布局组件:Sidebar、TopBar、MobileNav
单一职责:每个组件只做一件事容器/展示分离:逻辑在容器组件,展示在 UI 组件Props 向下,回调向上:数据通过 props 传入,事件通过回调通知父组件超过 300 行必须拆分。
lib/ 目录详解
lib/ 核心逻辑
lib/pyodide/executor.ts Pyodide Worker 管理创建/销毁 WorkerpostMessage 通信超时保护请求 ID 匹配lib/judge/engine.ts 判题引擎执行用户代码比对输出/返回值运行测试用例生成判题报告lib/progress/ 进度管理localStorage 读写XP/等级计算成就检测lib/store/ Zustand Store全局 UI 状态学习进度状态主题状态
Next.js App Router 中,动态路由参数用什么语法?
判题引擎 engine.ts 是不依赖 React 的纯业务逻辑,它应该放在哪个目录?
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
快速连续发起请求时,先发的慢响应可能晚到、反而覆盖了新结果,导致页面显示「过期数据」。标准解法是给每次请求一个递增序号,或用 AbortController 取消上一个请求,回调里只接受最新一次的结果。此外 loading、error、空数据这三种状态都要显式处理,别只写成功路径。
挑战任务
按职责给文件分层
写个 classify_layer(path) 函数:输入路径,根据前缀返回所属分层,可选值是 app / components / lib / hooks。这是关注点分离在目录层面的落地。
课后作业
分析组件依赖
选课程页面,从page.tsx开始,画出组件树,列出所有子组件及其Props。