在本站写文章:Markdown、GFM 与 MDC 语法指南

摘要

从 frontmatter 到代码组、提示框和折叠面板,完整介绍 Leisuer Blog 当前支持的文章语法与项目约定。

在本站写文章:Markdown、GFM 与 MDC 语法指南

本站的文章并不只是普通 Markdown。内容会先交给 Nuxt Content 解析,再由 Nuxt UI 的 prose 组件和项目中的自定义组件渲染。因此,除了标题、列表、链接这些基础语法,还可以使用代码高亮、文件名、行号、代码组、提示框和折叠面板。

这篇文章以项目当前的源码为准,整理一份可以直接复制使用的写作手册。

一篇文章的最小结构

每篇文章都由两部分组成:顶部的 frontmatter 和下面的 Markdown 正文。可以从这个模板开始:

---
title: 文章标题
description: 一句话摘要,也会作为页面的 SEO 描述。
created: 2026-09-30
updated: 2026-09-30
category: Notes
tags:
  - Markdown
  - Nuxt Content
cover: /img/cover/example.webp
pinned: false
draft: false
---

# 文章标题

这里开始写正文。

## 第一个章节

章节内容。

项目当前认识的自定义字段如下:

字段类型用途
title字符串页面标题、文章卡片标题和浏览器标题
description字符串文章摘要和 meta description
created日期发布时间
updated日期最后修改时间,可省略
category字符串或字符串数组单级或多级分类
tags字符串数组标签列表
cover字符串文章卡片封面地址
pinned布尔值是否在文章列表中置顶
draft布尔值是否从文章列表、分类、标签和搜索中隐藏

多级分类可以写成数组:

category: [Notes, Nuxt Content]

wordCount 和 readingMinutes 虽然也存在于内容 schema 中,但构建时会根据正文自动计算,通常不需要手写。

如果新增 frontmatter 字段,还要同步修改 content.config.ts 的 schema,否则 Nuxt Content 可能会丢弃这个字段。修改 schema 后,开发服务器通常也需要重启。

标题与目录

Markdown 使用 # 表示标题层级:

# 文章标题

## 一级章节

### 二级章节

#### 更深一层的标题

项目的文章壳会单独显示 frontmatter 中的 title,所以正文里的 # 一级标题 会被隐藏。保留它可以让 Markdown 文件脱离网站后仍有完整结构,但正文内容建议从 ## 开始。

页面会根据标题自动生成锚点和侧边目录。当前目录主要展示二级、三级标题,滚动时会同步高亮所在章节;标题层级应该依次递进,不要只为了字号从 ## 突然跳到 ####。

段落与行内格式

空一行会开始一个新段落。普通换行通常仍属于同一段;如果确实需要强制换行,可以在行尾输入两个空格,或者使用 <br>。

这是第一段,其中有 **粗体**、*斜体*、***粗斜体***、~~删除线~~ 和 `行内代码`。

这是第二段。  
这一行会强制换行。

也可以使用 HTML 换行。<br>
这里会显示在下一行。

常用的行内写法:

效果写法
粗体**重要内容**
斜体*强调内容*
粗斜体***特别强调***
删除线~~旧内容~~
行内代码`const answer = 42`
高亮<mark>高亮内容</mark>
键盘按键<kbd>⌘</kbd> + <kbd>K</kbd>

如果想原样显示 Markdown 符号,可以在它前面添加反斜杠:

\*这段文字不会变成斜体\*
\# 这也不会变成标题

链接与图片

行内链接、自动链接和引用式链接都可以使用:

[Nuxt 官网](https://nuxt.com)

<https://content.nuxt.com>

[Nuxt Content][content-docs]

[content-docs]: https://content.nuxt.com "Nuxt Content 文档"

站内链接可以直接写路径:

[查看关于本站](/posts/about-site)

图片建议放进 public/,并从站点根路径引用:

![图片替代文本](/img/example.webp)

![带标题的图片](/img/example.webp "图片标题")

Nuxt Content 的 MDC 属性还可以控制图片尺寸和类名:

![96 像素头像](/img/avatar.jpg){width="96" height="96"}

![圆角头像](/img/avatar.jpg){.w-24 .rounded-xl}

![限制最大宽度](/img/background.jpg){.max-w-md}

所有正文图片默认都会限制在文章宽度内、保持比例并居中显示。替代文本不能省略:除了无障碍访问,它也会在图片加载失败时提供必要信息。

引用、分隔线与列表

引用使用 >,可以包含多个段落或继续嵌套:

> 写作不是把所有内容堆在一起,而是帮助读者建立顺序。
>
> > 嵌套引用适合补充引用中的上下文。

三个连字符可以生成分隔线:

---

无序列表可以使用 -、* 或 +,有序列表使用数字:

- 第一项
- 第二项
  - 嵌套项目
  - 另一个嵌套项目

1. 第一步
2. 第二步
3. 第三步

任务列表使用 GFM 的复选框语法:

- [x] 已完成
- [ ] 待处理
- [ ] 支持 **粗体** 和 `行内代码`

缩进决定列表的嵌套关系。为了减少不同解析器之间的差异,建议每层统一缩进两个或四个空格。

表格

表格由竖线和表头分隔行组成,分隔行中的冒号控制对齐方式:

| 功能 | 状态 | 备注 |
| :--- | :---: | ---: |
| 普通 Markdown | 支持 | 左对齐 |
| MDC 组件 | 支持 | 居中 |
| 代码高亮 | 支持 | 右对齐 |
功能状态备注
普通 Markdown支持左对齐
MDC 组件支持居中
代码高亮支持右对齐

文章样式会给表头和隔行添加背景色;表格过宽时可以横向滚动,不会挤破页面。

代码块

三个反引号可以创建围栏代码块,在开头写语言名即可启用语法高亮:

```ts
type Post = {
  title: string
  tags: string[]
}

const post: Post = {
  title: 'Hello Nuxt Content',
  tags: ['Nuxt', 'Markdown'],
}
```

渲染后的代码块会自动带有语言标题、macOS 风格顶栏和悬停出现的复制按钮。项目额外配置了 C++ 高亮,也可以使用 js、ts、vue、bash、json、diff、css、html、md 等常见语言标识。

显示文件名

把文件名放在语言标识后的方括号中:

```ts [content.config.ts]
export default defineContentConfig({
  collections: {},
})
```

显示行号

在代码块元信息中加入 {.line-numbers}:

```ts [example.ts] {.line-numbers}
const answer = 42
console.log(answer)
```

如果示例来自原文件的第 8 行,可以在文件名后添加起始行号:

```ts [example.ts:8] {.line-numbers}
const answer = 42
console.log(answer)
```

代码组

同一操作有多种写法时,可以用 code-group 把它们组织成标签页:

::code-group
  ```bash [npm]
  npm install
  ```
  ```bash [pnpm]
  pnpm install
  ```
  ```bash [yarn]
  yarn install
  ```
::

实际显示:

npm install

缩进不是装饰:代码组中的每个代码块需要缩进到容器内部。

提示框

项目提供了四种提示框:note、tip、warning 和 caution。

::note
用于补充背景信息。
::

::tip{title="小技巧"}
用于给出更高效的做法。
::

::warning{title="请留意"}
用于提醒潜在问题。
::

::caution{title="危险操作"}
用于强调严重后果。
::

实际显示:

在组件名后使用 {title="..."} 即可传入标题。没有自定义标题时,提示框仍会保留对应的颜色和图标。 ::

提示框内部仍然可以继续使用 Markdown:

::warning{title="发布前检查"}
- [ ] 日期是否正确
- [ ] 图片是否有替代文本
- [ ] 链接是否能访问
::

折叠内容

需要隐藏补充说明或答案时,可以使用 collapse;希望默认展开时使用 expand:

::collapse{title="点击查看答案"}
这里的内容默认折叠。
::

::expand{title="默认展开的说明"}
这里的内容打开页面时就能看到,也可以手动收起。
::
点击查看一个实际示例

这是由项目中的 ProseCollapse 和 ProseDetails 组件渲染的内容。

  • 可以写多个段落
  • 也可以放列表与代码块
默认展开的实际示例

这个面板会默认展开,读者仍然可以点击标题将它收起。

details 是同一套底层组件的通用形式,还能传入 open、icon 和 type 等属性:

::details{title="通用详情面板" icon="lucide:book-open"}
这里使用的是 `details` 组件。
::

实际显示:

通用详情面板

这里使用的是 details 组件。日常写作仍建议优先选择语义更明确的 collapse 或 expand。

脚注

脚注适合放来源、解释或不希望打断正文的补充信息:

Nuxt Content 会把 Markdown 转换成可查询的结构化内容。[^content]

[^content]: 详见 Nuxt Content 官方文档。

脚注定义可以包含多行,后续行需要缩进:

这是一条较长的说明。[^long-note]

[^long-note]: 第一行内容。
    这是同一条脚注的后续内容。

原生 HTML 与 MDC 组件

Markdown 中可以嵌入少量原生 HTML。项目已经为 <details>、<summary>、<kbd>、<mark> 等元素准备了正文样式:

<details>
  <summary>原生折叠区域</summary>

  这里是折叠内容。
</details>

按 <kbd>⌘</kbd> + <kbd>K</kbd> 打开搜索。

<mark>这是一段高亮文字。</mark>

MDC 也可以直接调用已注册的 Vue 组件,属性写在花括号中:

::alert{color="#53a3f1"}
这是由 `app/components/Alert.vue` 渲染的内容。
::

::div{class="rounded-lg border p-4"}
这是带有 class 的原生 div 容器。
::

::counter
::

:badge[内联徽章]{color="info"}

::card
### 卡片标题

卡片中也可以继续使用 **Markdown**。
::

实际显示:

这是由 Alert.vue 渲染的提示区域,边框颜色来自传入的 color 属性。

这是一个通过 MDC 创建的 div,样式直接来自传入的 Tailwind class。

Counter: 0

上面的计数器是一个真正可交互的 Vue 组件,点击按钮后数字会立即变化。

行内 Badge 的效果::badge内联徽章

Card 的效果:

卡片标题

卡片中也可以继续使用 Markdown、行内代码 和链接。

这种能力很强,但也会让文章依赖项目内部实现。通用内容优先使用标准 Markdown;只有确实需要交互或特殊布局时,再使用自定义组件。

当前没有启用的语法

测试文档中还包含数学公式和定义列表,但项目目前没有配置数学公式插件或专门的定义列表渲染规则。下面的内容不应假定会得到数学排版:

$E = mc^2$

$$
E = mc^2
$$

术语
: 定义内容

它们可能只会作为普通文本显示。需要公式时,应先为 Nuxt Content 接入相应的 Markdown 插件并补充样式,再在正式文章中使用。

发布前检查清单

  • 文件位于 content/,路径和文件名清晰且稳定
  • frontmatter 至少包含 title、description、created、category、tags 和 draft
  • 正文结构从 ## 开始,标题层级连续
  • 图片使用 public/ 下的根路径,并填写替代文本
  • 代码块标注正确的语言,必要时补充文件名和行号
  • 重要说明选择了合适的提示框,而不是只靠加粗或颜色
  • 草稿已确认 draft 状态,且没有把私密内容当作草稿保存
  • 新增 frontmatter 字段时同步更新了 content.config.ts

掌握这些规则后,写作时只需要关心内容本身:基础结构交给 Markdown,复杂展示交给 MDC,文章元信息则统一放在 frontmatter。这样写出的文件既保持可读,也能完整使用本站的目录、分类、搜索、代码高亮和主题样式。