25 分钟
项目全解析

项目立项:从需求到技术选型

从需求分析开始,一步步推技术方案。

  • 从产品需求推技术方案,就这么来。
  • 掌握技术选型的决策框架
  • 学会评估方案的 trade-off
  • 了解朋友好学的完整开发流程

一切从需求开始

写第一行代码之前,先想三个事:要解决啥问题?有啥约束条件?怎么算成功?

🐍需求分析的三层结构

小白学Python,看视频走神,读书枯燥,刷题没反馈。要做个边学边练的网站,代码能即时跑,有游戏化激励。技术选型得对应这几层需求:浏览器里跑Python,不用后端,能离线用,还得适配手机。

核心技术挑战

这个项目最大的技术挑战是:如何在浏览器中运行 Python 代码?

ℹ️在浏览器里跑 Python 的 5 种方案对比

后端执行· 原理:代码发到服务器执行· 优点:功能完整· 缺点:服务器成本、安全风险、延迟PyPy.js· 原理:Python 编译为 JS· 优点:纯前端· 缺点:已停止维护、Python 版本老Skulpt· 原理:JS 实现的 Python· 优点:轻量· 缺点:只支持子集、无第三方库Pyodide(最终选择)· 原理:CPython 编译为 WASM· 优点:完整 Python 3.14· 缺点:首次加载约 10MB远程容器· 原理:Docker 隔离执行· 优点:任意依赖· 缺点:成本极高、复杂 最终选择 Pyodide:完整 CPython 3.14;支持 numpy / pandas 等;Web Worker 不阻塞 UI;零服务器成本;可被 Service Worker 缓存

💡技术选型的核心原则

别盯着「最好的」技术,选「最合适的」。Pyodide 10MB 体积是缺点,但做编程学习工具的话,用户愿意等 3 秒加载完整 Python 运行时,后续代码执行全是零延迟。要是做落地页,10MB 就不行。

技术栈选型决策

Python
# 每个技术选择的理由
print("""
框架:Next.js 16 (App Router)
  → SSR/SSG 提升首屏速度和 SEO
  → 文件路由,零配置
  → 静态导出,可部署到任何静态服务器

语言:TypeScript (strict)
  → 类型安全,重构有信心
  → 编辑器智能提示
  → 编译期捕获大量错误

样式:Tailwind CSS v4
  → 原子化类名,零运行时开销
  → 设计系统通过 CSS Variables 统一
  → 深色/浅色主题切换只需改变量

编辑器:Monaco Editor
  → VS Code 同款,用户熟悉
  → 语法高亮、自动补全、错误提示

状态管理:Zustand
  → 1KB 体积,API 极简
  → persist 中间件自动持久化

动画:Framer Motion
  → 声明式 API,手势支持

Python 运行:Pyodide v3.14 (Web Worker)
  → CPython 编译为 WASM
  → Worker 线程隔离,不卡 UI
""")

项目目录结构设计

好的目录结构,新开发者10分钟内能看懂项目全貌。

Python
# 朋友好学目录结构
print("""
pygood/
├── app/                    # Next.js App Router 页面
│   ├── page.tsx           # 首页(落地页)
│   ├── layout.tsx         # 根布局(字体、Provider、主题)
│   ├── providers.tsx      # 全局 Provider(React Query)
│   ├── globals.css        # 全局样式 + CSS 变量 + 动画
│   ├── dashboard/page.tsx # 学习仪表盘
│   ├── course/[id]/       # 课程详情页(动态路由)
│   ├── lab/page.tsx       # 代码实验室
│   └── profile/page.tsx   # 个人中心
├── components/             # React 组件
│   ├── ui/                # 基础 UI(Button/Card/Badge...)
│   ├── editor/            # 代码编辑器、编程键盘
│   ├── course/            # 课程内容渲染、挑战面板
│   ├── layout/            # 导航栏、侧边栏
│   └── ai/                # AI 助手
├── lib/                    # 核心逻辑(无 React 依赖)
│   ├── pyodide/executor.ts # Python 执行引擎
│   ├── judge/engine.ts    # 自动判题引擎
│   └── utils/             # 工具函数、课程数据、等级系统
├── hooks/                  # 自定义 React Hooks
├── store/                  # Zustand 状态管理
├── types/                  # TypeScript 类型定义
└── public/pyodide/        # Pyodide 运行时文件
""")
🐍为什么 lib/ 和 components/ 分开?

lib/ 目录放纯逻辑代码(不依赖 React),components/ 放 UI 组件。这么分的好处:逻辑代码能在任何地方复用逻辑代码更容易测试职责清晰,改逻辑不会意外影响 UI。这就是关注点分离的体现。

选择题

朋友好学选 Pyodide 不选后端跑 Python,最关键的原因是什么?

选择题

朋友好学中用户的 Python 代码运行在哪种线程?

资深工程师加餐

底层原理 · 大厂视角 · 工程经验,点卡片展开

快速连续发起请求时,先发的慢响应可能晚到、反而覆盖了新结果,导致页面显示「过期数据」。标准解法是给每次请求一个递增序号,或用 AbortController 取消上一个请求,回调里只接受最新一次的结果。此外 loading、error、空数据这三种状态都要显式处理,别只写成功路径。

挑战任务

技术选型:按综合分挑方案

简单+50 XP

选型不是选单项最强,而是选综合最优。每个候选方案是 (名称, 综合分)。请实现 best_choice(candidates):返回综合分最高的方案名称(并列时取第一个)。

技术选型:按综合分挑方案
1 个测试用例