- 前端
- 教程
【免费下载链接】preguntas-entrevista-react
Preguntas típicas sobre React para entrevistas de trabajo ⚛️
本文围绕仓库中的 manuscript/README.md 展开,讲解 preguntas-entrevista-react 项目如何把一份README.md面试题库与public/quiz/qa/下的测验数据,自动生成可供 Leanpub 出版平台直接使用的电子书手稿(manuscript)。读完本文,你将掌握pnpm book生成命令的完整调用链、手稿文件与源文件的映射关系、确定性随机的测验出题机制、以及针对 Leanpub/Pygments 的 Markua 代码围栏改写规则,并了解这套管线与网站内容生成(public/content/*.json)之间的分工。
一、manuscript 目录是什么:一份"不要手改"的生成产物
manuscript/README.md 开篇就明确了目录的定位:manuscript/下的所有文件都是自动生成的电子书手稿,目录内写着一句硬性约束——No edites estos ficheros a mano(不要手动编辑这些文件)。任何对书籍内容的修改都必须回到源头文件进行,再重新生成,否则下次构建会被整体覆盖。
以仓库当前实际内容看,生成出的手稿共有 8 个文件:
00-antes-de-empezar.md—— 序言(frontmatter),对应 book/before.md01-principiante.md—— 初级章(约 2865 行,含全部初级问题与测验)02-intermedio.md—— 中级章03-experto.md—— 高级章04-errores-tipicos.md—— "React 典型错误"章05-hasta-aqui.md—— 结语(backmatter),对应 book/after.mdBook.txt—— Leanpub 的章节装配清单README.md—— 本文所讲的这份生成说明文档本身也是脚本产物
这种"源文件 + 生成脚本 + 产物目录"的结构,保证了书籍内容与网站内容(后面会讲)可以同源维护,不会出现两处内容漂移。
二、生成命令与调用链:pnpm book 做了什么
在 package.json 的 scripts 中可以找到命令入口:
"scripts": { "markdown:lint": "node scripts/lint.mjs", "content": "node scripts/markdownToJson.mjs", "book": "node scripts/exportBook.mjs", "dev": "node scripts/markdownToJson.mjs && astro dev", "build": "node scripts/markdownToJson.mjs && astro build", "preview": "astro preview", "lint": "oxlint" }执行pnpm book实际上运行的是node scripts/exportBook.mjs。从 scripts/exportBook.mjs 顶部注释可以看到脚本职责:
- 解析根目录
README.md,按章节与问题拆解; - 读取
public/quiz/qa/下对应的 JSON 测验数据,为每题生成"Pon a prueba"测验块; - 把答案整理成每章末尾的 solucionario(答案页);
- 写入
manuscript/目录并生成Book.txt。
注意与网站构建的区别:dev和build运行的是 scripts/markdownToJson.mjs(负责把 README 切分成public/content/*.json供网页渲染),而书籍输出走的是独立的exportBook.mjs,两者读同一份README.md源,但产出完全不同——这正是"同源多端"设计。
三、内容来源与文件映射:四个源头如何拼成一本电子书
manuscript/README.md 给出了官方的源文件清单:
book/before.md→ frontmatter("Antes de empezar" 序言)book/after.md→ backmatter("Hasta aquí" 结语)README.md→ 全部问题与解答正文public/quiz/qa/*.json→ "Pon a prueba" 测验块 + solucionario
在 scripts/exportBook.mjs 中,章节的层级结构由LEVELS常量硬编码定义:
const LEVELS = [ { key: 'principiante', heading: 'Principiante', file: '01-principiante.md' }, { key: 'intermedio', heading: 'Intermedio', file: '02-intermedio.md' }, { key: 'experto', heading: 'Experto', file: '03-experto.md' }, { key: 'errores típicos en react', heading: 'Errores típicos en React', file: '04-errores-tipicos.md', }, ]脚本通过 parseReadme 对根 README.md 做结构化解析:
- 以
###开头的行视为章节标题,其文本(转小写)与LEVELS的key匹配; - 以
####开头的行视为单条问题,问题标题同时会被@sindresorhus/slugify转成 slug,例如 "¿Qué es React?" 变成que-es-react; - 每个问题的正文会清理掉导航链接(
**⬆ Volver a índice**)和末尾的---分隔线,并把连续空行压缩。
章节找不到对应内容时脚本会输出⚠ Capítulo sin contenido警告并跳过(见 scripts/exportBook.mjs),避免生成空章节。
在写入每个章节前,脚本还会检查book/before.md与book/after.md是否存在,缺失时直接抛出Falta el capítulo de introducción/cierre错误并终止构建(scripts/exportBook.mjs),从源码层面保证了序言与结语是手稿的强制组成部分。
四、章节骨架与生成后的手稿结构
每一章生成时,脚本先写入章节标题和引导语("scripts/exportBook.mjs"):
# Principiante Preguntas de nivel **Principiante**. Tras cada explicación puedes encontrar un bloque *Pon a prueba*; las respuestas están al final del capítulo.然后对每道题调用buildQuestionMarkdown(scripts/exportBook.mjs),按固定模板组装:
## 问题标题- 问题正文(Markdown,经过围栏改写)
##### Pon a prueba测验块- 章节之间用
---做软分隔(刻意不用硬分页,因为书很长)
实际产物可在 manuscript/01-principiante.md 中查看,例如:
## ¿Qué es React? **React es una biblioteca de JavaScript de código abierto para construir interfaces de usuario.** ... ##### Pon a prueba *Responde sin mirar el solucionario del final del capítulo. Marca una sola opción.* **1.** ¿Qué es React? - **a)** React es un lenguaje de marcado como HTML. - **b)** React es un servidor web. - **c)** React es una biblioteca de JavaScript para construir interfaces de usuario. - **d)** React es un framework para construir aplicaciones web.每个章节的末尾由formatChapterSolutions生成## Solucionario答案区,并插入{pagebreak}分页符(scripts/exportBook.mjs),保证读者在章节尾部才看到答案,不会被"剧透"。
五、"Pon a prueba"测验块的生成机制:1 个正确 + 3 个干扰项
这是手稿中最有工程含量的部分。Quiz 的原始数据存放在 public/quiz/qa/ 下,每个问题一个 JSON 文件。以 public/quiz/qa/que-es-react.json 为例,原始选项远多于 4 个,且带有is_correct标记。
脚本通过loadQuiz(slug)(scripts/exportBook.mjs)按问题 slug 查找对应 JSON,再经prepareBookQuestion(scripts/exportBook.mjs)加工成恰好 4 个选项:
const OPTIONS_PER_QUESTION = 4 // 1 correct + 3 distractors筛选逻辑是:取出is_correct为 true 的选项作为正确答案,再从干扰项中随机挑选 3 个,最终把 4 项打乱。关键点在于**"确定性随机"**——不是每次构建都变:
hashString用 FNV-1a 算法把问题 ID 哈希成一个种子(scripts/exportBook.mjs);mulberry32用该种子生成伪随机数序列(scripts/exportBook.mjs);seededShuffle用该序列做 Fisher-Yates 洗牌(scripts/exportBook.mjs)。
脚本注释对此有明确说明:"stable builds, not always answer 'a'"——同样的源数据每次构建都会得到完全相同的题目排列与答案字母,PDF 在线阅读与打印版行为一致,同时避免正确答案永远停留在 a 选项的尴尬。这是从源码结构可以确认的设计意图。
若某个问题在 quiz 目录下没有对应 JSON,或 JSON 为空数组/缺alternatives,脚本会静默跳过该题的测验块(返回null),只保留正文。
六、Markua 代码围栏改写:让 Leanpub/Pygments 正确高亮 JSX
manuscript/README.md 专门强调了一个 PDF 排版细节:Leanpub 使用 Pygments 做语法高亮,但很多 Leanpub 构建版本没有内置 JSX lexer,如果代码围栏写成```jsx或```tsx,PDF 会以纯等宽文本输出、完全没有颜色。
因此exportBook.mjs在把问题正文写入手稿前,会先经过normalizeCodeFencesForLeanpub(scripts/exportBook.mjs)做全局正则改写,映射表LEANPUB_LANG_MAP(scripts/exportBook.mjs)定义如下:
| 源围栏语言 | 改写为 |
|---|---|
jsx | javascript |
tsx | typescript |
js/mjs/cjs/react | javascript |
ts | typescript |
shell/sh/zsh/console | bash |
text/plain/txt | text |
| 其他未知语言 | 原样保留 |
改写时只处理语言 ID 部分,像```jsx title=...这种带附加信息的 info 串会保留title=...部分(scripts/exportBook.mjs),避免破坏 Markua 的其他围栏特性。
源码注释也坦承了这一权衡:javascriptlexer 对</div>这类 JSX 闭合标签着色并不完美,但总比完全没有高亮要好得多。换言之,为了在 Leanpub 的 Pygments 环境下获得可用的高亮,选择了牺牲少量 JSX 精确度。如果你在 Generation Settings 中发现 PDF 代码没有颜色,优先检查两点:围栏是否已被改写成javascript/typescript,以及语法高亮开关是否已打开。
七、Book.txt:Leanpub 的章节装配清单
Leanpub 依靠手稿目录下的Book.txt决定章节顺序与主/辅内容分区。脚本构建时动态生成它(scripts/exportBook.mjs):
frontmatter: 00-antes-de-empezar.md mainmatter: 01-principiante.md 02-intermedio.md 03-experto.md 04-errores-tipicos.md backmatter: 05-hasta-aqui.md这就是仓库中 manuscript/Book.txt 的实际内容:frontmatter(序言)、mainmatter(四个章节)、backmatter(结语)三个分区各就各位。上传到 Leanpub 后,平台会严格按此顺序组装书稿。
八、上传 Leanpub 与生成设置
manuscript/README.md 给出的最后一步是:把manuscript/文件夹上传到 Leanpub(或根据你自己的流程用 Dropbox/Git 同步),并建议在Generation Settings 中确认 syntax highlighting 处于开启状态——这与第六节讲的 Pygments 围栏改写是配套动作:围栏语言对了 + 高亮开关开了,PDF 才有彩色代码。
需要提醒的实践要点:
- 因为
manuscript/是纯生成产物,上传前务必先执行pnpm book重新生成,保证与最新源内容一致; - 若用 Git 同步方式发布,把
manuscript/与book/、README.md、public/quiz/qa/一起纳入版本管理,便于追溯每次电子书构建对应的源状态; - 脚本每次运行会先
emptyDir(OUT_DIR)清空手稿目录(scripts/exportBook.mjs),所以任何对手稿的手动修改都会在下次构建时被删除,这是"不要手改"约束的强制保障。
九、与网站内容管线的分工:一份源,两种产物
虽然本文主题是书籍手稿,但理解它与网站生成的边界有助于正确使用本仓库。网站侧的 scripts/markdownToJson.mjs 做的是:
- 读取根 README.md;
- 用 Prism 做代码高亮(代码块正则匹配 + 动态加载
prismjs/components/prism-<lang>.js,找不到的语言降级为txt,见 scripts/markdownToJson.mjs); - 按
###/####切分,把每道题输出为一个public/content/<slug>.json(含id、level、title、content),并汇总index.json与counter.json; - 级别映射关系
principiante→EASY、intermedio→MEDIUM、experto→HARD、errores típicos en react→ERRORS(scripts/markdownToJson.mjs)。
对比可见:
- 书籍管线(
exportBook.mjs):面向 Leanpub 静态排版,聚合 quiz 数据生成测验与答案页,做 Pygments 围栏改写; - 网站管线(
markdownToJson.mjs):面向浏览器交互,用 Prism 高亮并切分出逐题的 JSON 数据,供 src/pages/questions.astro 等页面渲染。
两者共享同一份 README.md 与 public/quiz/qa/ 数据源,因此"改一次源、书籍与网站同步更新"。唯一的区别是网站不会用到book/before.md与book/after.md(脚本注释明确标注它们是 "Exclusive book chapters (not used on the website)",见 scripts/exportBook.mjs)。
十、贡献内容时应改哪里
如果你发现某道题答案有误,或想新增面试题,正确操作是改根目录 README.md(题目正文)与 public/quiz/qa/ 下对应 slug 的 JSON(测验选项),然后本地运行pnpm book验证手稿生成无误,再提交改动。手稿文件本身不应进入改动范围——正如 scripts/lint.mjs 只对README.md做 markdownlint 校验所示,源文件才是仓库内容的唯一真源。
这套管线将"内容维护"与"出版输出"彻底解耦:面试题库以纯 Markdown 单文件形式维护,工程上却同时驱动了 Leanpub 电子书、交互式测验与静态网站三种消费形态,是内容型开源项目值得参考的同源多端构建范式。
- 前端
- 教程
【免费下载链接】preguntas-entrevista-react
Preguntas típicas sobre React para entrevistas de trabajo ⚛️
相关推荐
5步构建企业级数据表格应用:shadcn-table深度解析与实战指南
5步构建企业级数据表格应用:shadcn table深度解析与实战指南 在当今数据驱动的应用开发中,功能强大、性能卓越的数据表格组件已成为现代Web应用的核心需
前端教程Preguntas de entrevista para React:从零到专家的React面试题详解
Preguntas de entrevista para React:从零到专家的React面试题详解 React作为当前最流行的前端框架之一,已成为前端开发者
前端教程React面试题深度剖析:Preguntas de entrevista para React原理与实践
React面试题深度剖析:Preguntas de entrevista para React原理与实践 React作为前端开发领域的主流框架,其面试题往往涉及
前端教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考