N16第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

MDX 与内容系统

MDX 与内容系统 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

课程正文如果硬编码进组件,改一个错别字都要动代码。本课用 MDX 把内容变成文件:正文、代码、提示写进 .mdx,元数据走 frontmatter 校验,目录从标题自动生成,坏内容和死链要有明确的处理姿势。

问题场景

你给课程页加「本章小结」,做法是在 page.tsx 里塞一段 <p>。两周后正文里散落了 20 处这种「内容钉在代码里」的补丁——改文案要动组件、不会写代码的人改不了、也没法像文件一样做 diff 和备份。内容该是内容,代码该是代码。

核心解释

MDX = Markdown + 少量组件。 课程正文存成文件(如 content/courses/nextjs/n16-mdx-content-system/lessons/main.mdx),frontmatter 写元数据:

---
title: 流式渲染与加载边界
est_minutes: 25
knowledge_points: [Suspense, loading.tsx]
---

## 核心解释

流式渲染让快内容先走……

<aside>提示:本节代码在 app/courses 下。</aside>

元数据必须校验,坏数据挡在渲染前。 frontmatter 是人肉编辑的 YAML,一个 typo 就能让页面拿到 undefined。用 zod 在加载层把关:

// lib/content/loader.ts(节选)
import fs from "node:fs";
import matter from "gray-matter";
import { z } from "zod";

const FrontmatterSchema = z.object({
  title: z.string().min(1),
  est_minutes: z.number().int().positive().max(120),
  knowledge_points: z.array(z.string()).default([]),
});

export function loadLesson(mdxPath: string) {
  const raw = fs.readFileSync(mdxPath, "utf8");
  const { data, content } = matter(raw);
  const parsed = FrontmatterSchema.safeParse(data);
  if (!parsed.success) {
    // 内容系统的原则:坏内容要「可见地失败」,而不是渲染出空页
    throw new Error(`frontmatter 不合法: ${mdxPath} ${parsed.error.message}`);
  }
  return { meta: parsed.data, content };
}

目录从标题长出来,内部引用要体检。 渲染前先剥离代码围栏,再用正则扫 ^##? 抽出标题文本和锚点,生成右侧目录;正文里指向其他课程、课时的链接,在内容校验阶段逐个核对目标存在——指向不存在课时的死链要么在构建时报错拦下,要么降级标注「待补充」,别等用户点进去 404。

三条纪律:

  • 无效内容分场景处理:构建期能发现的(frontmatter 缺字段、死链、围栏不闭合)直接报错;运行时才出现的降级展示并留日志。
  • MDX 里用到的自定义组件(提示框、对照表)在渲染端统一注册,正文只管使用。
  • 内容文件进 git——改文案也有 diff 可查,这是内容离开组件库的回报。

示例拆解

在本机工程里:

  1. 打开 content/courses/ 下任意课程的 main.mdx,删掉 frontmatter 的 title 字段,运行内容校验(npm run content:check 或启动 dev)——应在加载层收到明确报错而不是白屏。
  2. 恢复 title,在正文里写一个指向不存在课程的路由链接,跑校验确认被标记为死链。
  3. 打开课程页,确认右侧目录条目与正文 ## 标题一一对应,代码块里的 # 注释没有混进目录。

关键点:npm run content:check 是内容质量的门禁——它跑得越勤,坏内容漏到页面的机会越少。

动手任务

在本机工程里完成:

  1. 给 loader 的 schema 加一条规则(如 est_minutes 上限),构造越界数据验证报错信息可读。
  2. 从一份 main.mdx 生成目录数组,打印确认层级与锚点正确。
  3. 给内部链接写一个核验函数:有效目标通过、无效目标报错,接进内容校验脚本。

常见错误

  • frontmatter 不校验直接用:拼写错误静默变成 undefined,页面某个角落悄悄坏掉。
  • 目录正则扫到了代码块里的井号注释:先剥离围栏再抽标题,否则目录里混进假标题。
  • 死链只在运行时暴露:用户点击才 404;链接核验必须进构建或校验流程。
  • 正文里堆复杂 JSX 逻辑:内容文件的编辑门槛反而变高;正文里只该出现少量展示型组件。

检查与复盘

自查清单:

  • 删改 frontmatter 字段会得到明确报错而非白屏
  • 目录、提示、代码高亮都来自内容文件本身
  • 内容校验脚本已进入日常流程(启动或提交前)

参考资料

检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选简单

知识站的课程正文采用 MDX 文件(content/courses/ 下)而不是存进数据库。这样做的主要好处是?

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...