25 分钟
项目全解析

目录结构与文件职责

就做一件事:把项目按文件夹拆解开,搞明白每个目录是干啥的。

  • 掌握项目目录结构
  • 理解每个文件夹的职责
  • 了解 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、空数据这三种状态都要显式处理,别只写成功路径。

挑战任务

按职责给文件分层

简单+50 XP

写个 classify_layer(path) 函数:输入路径,根据前缀返回所属分层,可选值是 app / components / lib / hooks。这是关注点分离在目录层面的落地。

按职责给文件分层
1 个测试用例

课后作业

分析组件依赖

中等+25 XP

选课程页面,从page.tsx开始,画出组件树,列出所有子组件及其Props。

分析组件依赖
1 个测试用例