freeCodeCamp 课程挑战文件格式解析:以椭圆(Ellipses)视频挑战为例
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
本篇以 freeCodeCamp 课程仓库中的挑战文件 ellipses-video.md 为主体,讲清一个challengeType: 11(video 类型)挑战文件的完整字段结构、它在课程块(block)中的组织方式、Joi 校验规则与前端渲染链路。读完后你能掌握:如何编写符合 schema 的视频讲解类挑战、如何用 frontmatter 与分区标记组织题目与视频答案,以及该文件涉及的椭圆数学知识点(标准方程与关键特征识别)。
一、挑战文件本体:Ellipses 视频挑战
该文件位于高级三角与圆锥曲线(advanced-trig-conics)块目录下,与同块的其他视频挑战(如 抛物线挑战、双曲线挑战)并列存放,全部为*-video.md命名:
curriculum/challenges/english/blocks/advanced-trig-conics/ ├── solving-right-triangles-video.md ├── law-of-cosines-video.md ├── parabolas-vertex-focus-directrix-video.md ├── ellipses-video.md ├── hyperbolas-video.md ├── polar-coordinates-video.md ├── parametric-equations-video.md └── difference-quotient-video.md文件由 YAML frontmatter 和正文分区两部分构成。frontmatter 定义挑战的元数据:
--- id: 699e85648f18cf77dfce5c23 title: Ellipses challengeType: 11 videoId: AwBM2AMwfu8 dashedName: ellipses ---各字段含义如下:
| 字段 | 取值 | 说明 |
|---|---|---|
id | 699e85648f18cf77dfce5c23 | 课程数据的主键,24 位 ObjectId 格式 |
title | Ellipses | 展示给学习者的挑战标题 |
challengeType | 11 | 挑战类型,11表示 video(视频讲解)挑战 |
videoId | AwBM2AMwfu8 | 嵌入播放器的视频 ID(YouTube 视频 ID) |
dashedName | ellipses | 小写连字符形式的 URL 名,用于生成学习路径 |
正文使用自由课程(freeCodeCamp 的 Markdown 挑战格式)的分区标记:
# --description--:介绍本视频的学习目标——椭圆的标准方程(standard equations)以及如何识别椭圆的关键特征;# --questions--内部以## --text--给出题目文本,## --answers--中以---分隔符列出多个候选答案,## --video-solution--给出正确答案在答案列表中的序号(从 1 开始)。
本挑战的题目与四个候选答案为:
题目:What is an ellipse?(什么是椭圆?)
- The set of points such that the sum of the distances from two fixed points is constant.(到两个定点距离之和为常数的点的集合。)——正确答案
- The set of points such that the distance from one fixed point is constant.(到一定点距离为常数的点的集合——这是圆的定义。)
- The set of points such that the product of the distances from two fixed points is constant.(到两个定点距离之积为常数的点的集合。)
- The set of points equidistant from a line and a point.(到一条直线和一个定点距离相等的点的集合——这是抛物线的定义。)
--video-solution--中的1表示第 1 个答案是正确项。对照同块的双曲线挑战 hyperbolas-video.md,其四个选项与本题高度对称(双曲线对应“两点距离之差为常数”),--video-solution--为2——这种题型设计是圆锥曲线三兄弟(圆、椭圆、双曲线)与抛物线定义辨析的典型考法。
二、椭圆知识要点:从挑战描述展开
挑战的 description 明确了三个学习要点:椭圆本身、标准方程、关键特征的识别。这正是题目答案所考查的核心定义,配合标准方程可以展开如下。
定义(即本挑战的正确答案):椭圆是平面上到两个定点(焦点,foci)距离之和为常数的点的集合,该常数记为 2a,且必须大于两焦点间距离 2c(即 a > c > 0)。
标准方程(以中心在原点为例):
- 焦点在 x 轴上:x²/a² + y²/b² = 1(a > b > 0)
- 焦点在 y 轴上:x²/b² + y²/a² = 1(a > b > 0)
其中 b² = a² − c²,a 为长半轴长,b 为短半轴长,c 为半焦距。
关键特征识别方法:
| 特征 | 如何由标准方程读出 |
|---|---|
| 中心 | 方程中的平移量(原点则为 (0, 0)) |
| 焦点所在轴 | 比较 a² 与 b² 的分母位置,较大的分母所在轴即焦点所在轴 |
| 顶点 (vertices) | 焦点轴上的端点,距中心为 a |
| 共顶点 (co-vertices) | 短轴端点,距中心为 b |
| 焦点 | 焦点轴上距中心为 c 的两点,c = √(a² − b²) |
| 离心率 | e = c/a,0 < e < 1,越接近 1 椭圆越扁 |
题目中四个干扰项分别对应圆(一定点等距)、距离积为常数曲线、抛物线(点到点与到线等距)的定义,这正是“识别椭圆”时最容易混淆的边界。
三、挑战在课程块中的注册:challengeOrder
视频挑战文件不是孤立的,它还必须在块结构文件中按 id 注册。本块的结构文件是 advanced-trig-conics.json:
{ "blockLabel": "lecture", "blockLayout": "challenge-list", "isUpcomingChange": false, "dashedName": "advanced-trig-conics", "helpCategory": "General", "challengeOrder": [ { "id": "699e854b8f18cf77dfce5c1e", "title": "Solving Right Triangles" }, { "id": "699e85518f18cf77dfce5c1f", "title": "Law of Cosines" }, { "id": "699e85568f18cf77dfce5c20", "title": "Law of Cosines - Old Version" }, { "id": "699e855a8f18cf77dfce5c21", "title": "Law of Sines" }, { "id": "699e855e8f18cf77dfce5c22", "title": "Parabolas - Vertex, Focus, Directrix" }, { "id": "699e85648f18cf77dfce5c23", "title": "Ellipses" }, { "id": "699e85688f18cf77dfce5c24", "title": "Hyperbolas" }, { "id": "699e856c8f18cf77dfce5c25", "title": "Polar Coordinates" }, { "id": "699e85718f18cf77dfce5c26", "title": "Parametric Equations" }, { "id": "699e8ca68f18cf77dfce5c27", "title": "Difference Quotient" } ] }几个要点:
blockLabel: "lecture"表明这是一个讲解型块,blockLayout: "challenge-list"表示在前端以挑战列表形式逐题推进;challengeOrder数组决定了学习顺序,Ellipses(id 与 md 文件 frontmatter 中的 id 完全一致)排在“Parabolas”之后、“Hyperbolas”之前,形成圆锥曲线的连续讲解链;- 新增一个视频挑战因此需要两件事:在
curriculum/challenges/english/blocks/<block>/下编写 md 文件,并在此块 json 的challengeOrder中以正确的 id 注册(本块 id 前缀均为699e85...,说明同块挑战是批量生成同一批次的)。
四、Schema 校验:video 挑战字段的硬性约束
课程仓库用 Joi 对所有挑战数据做严格校验,核心实现在 challenge-schema.js。与本挑战直接相关的约束有:
1. challengeType 是 0–33 的整数(challenge-schema.js#L180):
challengeType: Joi.number().min(0).max(33).required(),2. 类型 11 = video,由共享包统一定义(challenge-types.ts#L13):
const video = 11;同一文件还声明了该类型的视图与提交方式:viewTypes中[video]: 'generic'(走通用挑战渲染视图,challenge-types.ts#L117),submitTypes中[video]: 'tests'(完成后按测试类流程提交,challenge-types.ts#L159),并且 video 属于hasNoSolution列表——视频挑战没有可展示的标准代码解法。
3. video 挑战专属字段(challenge-schema.js#L293-L313):
// video challenges only: videoId: Joi.when('challengeType', { is: [challengeTypes.video], then: Joi.string().required() }), videoLocaleIds: Joi.when('challengeType', { is: challengeTypes.video, then: Joi.object().keys({ espanol: Joi.string(), italian: Joi.string(), portuguese: Joi.string() }) }), bilibiliIds: Joi.when('challengeType', { is: challengeTypes.video, then: Joi.object().keys({ aid: Joi.number().required(), bvid: Joi.string().required(), cid: Joi.number().required() }) }),也就是说:videoId是 video 挑战的必填项(本文件的AwBM2AMwfu8正因此必需);videoLocaleIds可按西语/意语/葡语覆盖默认视频;bilibiliIds(aid/bvid/cid 三元组)为中文环境提供备用播放源。本 Ellipses 挑战只声明了默认videoId,未配置多语言覆盖。
4. 题目结构(challenge-schema.js#L89-L102):
const questionJoi = Joi.object().keys({ text: Joi.string().required(), answers: Joi.array() .items( Joi.object().keys({ answer: Joi.string().required(), feedback: Joi.string().allow(null), audioId: Joi.string().allow(null) }) ) .required() .unique('answer'), solution: Joi.number().min(1).max(Joi.ref('..answers.length')).required() });以及 questions 数组对 video 类型要求至少 1 题(challenge-schema.js#L314-L322):
questions: Joi.when('challengeType', { is: [challengeTypes.video, challengeTypes.multipleChoice, challengeTypes.theOdinProject], then: Joi.array().items(questionJoi).min(1).required(), otherwise: Joi.array().length(0) }),对照本文件:--answers--中四个答案文本互不相同,满足unique('answer');--video-solution--的1满足solution必须在[1, answers.length]区间内。答案条数不固定(schema 只要求非空且唯一),但solution越界会直接校验失败——这是编写此类挑战时最容易踩的坑。
五、前端渲染链路:videoId 如何变成播放器
从源码结构看,video 挑战(viewType 为generic)由通用视图 show.tsx 渲染,其中引用了视频播放器组件 video-player.tsx。关键逻辑:
if (bilibiliIds && ['chinese', 'chinese-traditional'].includes(clientLocale)) { const { aid, bvid, cid } = bilibiliIds; bilibiliSrc = `//player.bilibili.com/player.html?aid=${aid}&bvid=${bvid}&cid=${cid}`; } if (videoLocaleIds) { const localeId = videoLocaleIds[clientLocale as keyof VideoLocaleIds]; videoId = localeId || videoId; }即:优先按客户端语言选择 B 站 iframe(仅中文/繁体中文环境且配置了bilibiliIds时)或对应语言版本的 YouTube 视频 ID;否则回退到默认videoId,通过react-youtube以rel: 0(禁用相关视频推荐)嵌入播放(video-player.tsx#L66-L80)。另外播放倍速会被写入本地存储(fcc-yt-playback-rate),学习进度以“视频加载完成 + 题目答对”为准。
对 Ellipses 挑战来说,它只声明了默认videoId: AwBM2AMwfu8且无bilibiliIds,因此所有语言环境都嵌入同一个 YouTube 视频,用户观看后答对那道定义选择题即完成本挑战,并在 advanced-trig-conics.json 定义的学习序列中解锁下一题(Hyperbolas)。
六、小结:一个视频挑战文件的完整契约
综合 frontmatter、schema 与渲染端,一个可被课程管线接受的 video 挑战(以 ellipses-video.md 为范本)需要满足:
- frontmatter 提供合法 ObjectId
id、challengeType: 11、必填videoId、连字符式dashedName; - 正文含
# --description--、# --questions--(至少一题,选项互不相同)、## --video-solution--(1 起的序号,不超过选项数); - 在所属块的结构 json(
curriculum/structure/blocks/*.json)的challengeOrder中按 id 注册,且 id 与文件内一致; - 可选地配置
videoLocaleIds(espanol/italian/portuguese 视频)与bilibiliIds(aid/bvid/cid)做多语言播放源。
这份文件本身承载的数学内容——椭圆“两焦点距离和为定值”的定义、标准方程与特征识别——则是 freeCodeCamp 在数学课程线中用“视频讲解 + 概念选择题”这一最轻量的挑战形态来传递知识点的典型样本。
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考