MDC(Markdown Components)让 Markdown 可以使用 Vue 组件。普通段落、列表和代码块保持原有写法,组件则负责承载属性、插槽和交互。语法基础见 Nuxt Content 的 MDC 文档。

本站通过 Nuxt Content 读取 .md 文件,MDC 语法直接写在同一个文件中,无需改成 .mdc 或 .mdx。本文中的原生元素、数据绑定、正文图片预览与代码组可以直接查看效果;教学用自定义组件仍会注明接入条件。

Markdown、MDC 与组件

写法用途使用条件
Markdown段落、标题、列表、图片和代码块当前可用
MDC 属性和容器给内容添加属性,用容器组织 Markdown当前可用
MDC 数据绑定在正文引用文章数据当前可用
MDC 自定义组件使用组件属性、插槽和交互需要对应 Vue 组件
代码组在多个代码片段之间切换本站已接入
折叠块收起补充内容,支持嵌套和锚点定位本站已接入

MDC 面向 Vue 组件,不使用 MDX 的 JSX 导入写法。组件名本身不会创建功能:在文章中写一个名称之前,需要先确认站点提供了对应组件。

本站的 ::collapse{title="展开说明"} 默认收起,:open="true" 可初始展开,详见折叠内容用法与示例。

块级语法

块级容器以 ::名称 开始,以单独一行 :: 结束。里面可以继续写 Markdown,容器与周围正文之间保留空行。容器内最后一段是列表时,在结束标记前也留一个空行。

下面使用原生 div,因此不依赖额外组件。

源码

md
::div
这里是容器内的 **Markdown 内容**。

- 标题来自文章 Meta。
- 正文继续使用 Markdown。

::

效果

这里是容器内的 Markdown 内容。

  • 标题来自文章 Meta。
  • 正文继续使用 Markdown。

如果有一个已注册的 ExamplePanel.vue,可以用 ::example-panel 调用它。这个名称只是下文的教学示例,当前项目没有安装该组件。

行内语法与属性

行内组件使用一个冒号,例如 :span[文字],在前面保留空格以分隔正文。仅需给一段文字添加属性时,可以使用 [文字]{属性}。

源码

md
当前示例: :span[已校对]。

[把指针放在这段文字上]{title="这是通过 MDC 设置的 title 属性"}。

效果

当前示例: 已校对。

把指针放在这段文字上。

链接、图片、强调和行内代码也可以带属性。以下链接通过 target 指定在新标签页打开。

md
[Nuxt Content](https://content.nuxt.com){target="_blank"}

Nuxt Content

{.类名 #标识符} 可以指定 class 和 id;class 对应的样式仍须由站点提供,不能仅凭名称产生效果。

给组件传递 Props

Props 是组件接收的参数。以下示例都假设已经注册了 ExamplePanel,只展示源代码。

行内参数

参数少时,可以直接放在花括号中:

md
::example-panel{title="环境要求"}
请先确认项目的运行环境。
::

字符串使用引号包裹。数值、布尔值、数组和对象应使用组件声明的类型;复杂数据可以使用 YAML 参数块,或使用冒号前缀传入 JSON。

YAML 参数块

参数较多时,将它们写在组件开头的两行 --- 之间。这一块只属于当前组件,与文件顶部的文章 Meta 分开。

md
::example-panel
---

title: 环境要求
---

请先确认项目的运行环境。
::

数组与对象

下面用假设的 ExampleList 展示结构化参数。它需要声明并处理相应的 items 和 options Props。

md
::example-list{:items='["Markdown", "MDC"]' :options='{"compact": true}'}
::

带冒号的属性可以解析 JSON,因此外层用单引号,JSON 字符串内部保留双引号。属性值的最终类型应与组件定义一致。

默认插槽与具名插槽

默认插槽承载组件内的主要内容;具名插槽用 #插槽名 切分不同区域。插槽标记属于组件内部,不是普通章节标题。

下面的 ExamplePanel 使用默认插槽和 footer 插槽:

md
::example-panel{title="发布检查"}
请确认图片和链接可以正常访问。

#footer
[阅读 Markdown 教程](/posts/markdown/markdown)
::

若要自行接入这个教学组件,可以创建 apps/website/app/components/content/ExamplePanel.vue,参考以下实现:

vue
<script setup lang="ts">
defineProps<{ title: string }>();
</script>

<template>
  <section class="my-6 border border-line rounded-xl bg-surface p-5">
    <p class="text-heading font-semibold">{{ title }}</p>
    <slot />
    <footer v-if="$slots.footer" class="mt-4 border-t border-line pt-4">
      <slot name="footer" />
    </footer>
  </section>
</template>

Nuxt Content 会全局注册 components/content/ 下的内容组件;放在其他目录时,需要另外配置全局注册。项目是 Nuxt 4 目录结构,这里的组件目录位于 app/ 下。

插槽默认保留 Markdown 生成的段落包装。如果组件需要把插槽文字直接放入标题等元素中,可以使用 <slot mdc-unwrap="p" /> 去除段落包装。具体行为见 Nuxt Content 的 Slot 文档。

容器嵌套

嵌套时,外层使用更多冒号,让每个结束标记对应到正确的层级。

源码

md
:::div
这是外层内容。

::div
这是内层内容,可以继续使用 **强调**。
::

这是回到外层后的内容。
:::

效果

这是外层内容。

这是内层内容,可以继续使用 强调。

这是回到外层后的内容。

同样的结构也适用于已经注册的 Vue 组件。每一层都应正确闭合,避免后续章节被误收进容器。

绑定文章数据

使用双花括号可以引用当前文章的数据。$doc 表示渲染时可访问的文档数据。

源码

md
本文标题:{{ $doc.title }}

发布日期:{{ $doc.publish }}

效果

本文标题:MDC 教程

发布日期:1970-01-01

Props 也可以绑定文档数据。以下写法需要先接入上文的 ExamplePanel:

md
::example-panel{:title="title"}
这里的标题取自文章的 title 字段。
::

如果要查询新的自定义字段,需要扩展内容集合的 schema 和校验规则。数据绑定不等于在 Markdown 中编写完整 Vue 模板;业务逻辑应放在组件中。

Nuxt Content 代码语法

语言、文件名与行号标记

代码围栏可以携带语言、文件名和需要强调的行号。这些内容会被解析成代码组件的参数,详见 Nuxt Content 的 ProsePre 文档。

源码

md
```ts [article.ts]{2}
const title = "MDC 教程";
const published = true;
const tags = ["MDC", "Nuxt Content"];
```

当前效果

article.ts
const title = "MDC 教程";
const published = true;
const tags = ["MDC", "Nuxt Content"];
标记传递的信息本站当前效果
ts代码语言用于语法着色
[article.ts]文件名显示为代码块标题或代码组标签
{2}第二行的强调标记显示强调背景与侧边标记

本站代码块已显示文件名、行强调和复制按钮;没有文件名时显示语言名称。复制保留原始缩进和换行,失败时会提示手动选择代码。语法高亮随站点深浅主题切换。

代码组

使用 ::code-group 包住多个代码块。文件名标记作为标签名称,没有文件名时使用语言名称。

源码

md
::code-group

```bash [安装依赖]
vp install
```

```bash [运行检查]
vp run check
```

::

效果

vp install

方向键切换标签,Home / End 移动到首尾。复制按钮只复制当前面板,各个代码组独立切换。切换标签后,原面板的横向滚动位置保留。

下面第二个代码组展示同一条配置的两种写法,可用来核对各组状态互不影响。

const notices = {
  staleAfterDays: 365,
};

图片展示与预览

标准 Markdown 图片自动支持预览,可填写尺寸和可选图注。提供宽高可预留展示空间,减轻图片加载后的布局变化。

源码

md
![文章内容处理流程](/images/content-example.svg){width="1200" height="675" caption="Markdown 经 Nuxt Content 解析后渲染为文章页面。"}

效果

Markdown 经 Nuxt Content 解析后渲染为文章页面。

点击图片打开预览,滚轮或双指手势缩放,放大后可拖拽。工具栏提供放大、缩小、复位和关闭;键盘使用加减号、方向键、0 和 Escape。缩放范围为初始适应尺寸的 1–4 倍。

通过布尔参数 preview 关闭单张图片的预览:

md
![静态示意图](/images/content-example.svg){:preview="false"}

图片被链接包裹时,继续执行链接跳转,不打开预览:

md
[![阅读 Markdown 教程](/images/content-example.svg)](/posts/markdown/markdown)

封面和头像不受此功能影响。图片网格、前后切图和作品卡片尚未提供。

GitHub 仓库卡片

使用公开仓库的 owner/name 标识。以下源码可直接复制,结束标记 :: 必须保留;卡片不接收插槽内容。

源码

md
::github{repo="nuxt/content"}
::

效果

nuxt/contentThe file-based CMS for your Nuxt application, powered by Markdown and Vue components.3.7K743MITTypeScript在新标签页打开 GitHub 仓库

卡片显示头像、仓库名称、简介、Star、Fork、许可证和主要语言,点击整张卡片在新标签页打开 GitHub。文章与关于页均可使用,普通 GitHub 链接不会自动转换。

仓库信息在站点构建时生成快照,随下一次构建更新;阅读页面时不请求 GitHub API。未取得的字段显示 -,实际计数为零时显示 0。头像加载失败时显示本地图标。

视频卡片

使用 Bilibili 与 Youtube 组件嵌入视频。卡片支持懒加载、全屏与响应式宽度,默认不自动播放:

md
::bilibili{bvid="BV1fK4y1s7Qf" title="Bilibili 视频示例"}
::

::youtube{id="5gIf0_xpFPI" title="YouTube 视频示例"}
::

也可通过 src 传入视频链接。完整效果、分 P 与开始时间参数见 在文章中嵌入视频。

正文提醒框

使用 ::alert 展示说明、建议或警告。正文支持 Markdown;标题是纯文本,省略时显示对应类型的默认标题。

源码

md
::alert{theme="github" type="important" title="先运行检查"}
提交前运行 `vp run check`,确认类型与格式检查通过。
::

::alert{theme="obsidian" type="check" title="补充说明" collapsible :open="false"}
点击标题展开,也可以使用键盘 Enter 或空格。
::

效果

先运行检查

提交前运行 vp run check,确认类型与格式检查通过。

四种风格为 github、obsidian、vitepress 和 docusaurus。省略 theme 时使用 site.article.alerts.theme,默认是 GitHub;省略 type 时使用 note。开启 collapsible 后默认收起,可通过 :open="true" 初始展开,字符串 open="false" 也能正确解析。

完整类型表、各风格效果、全站配置和嵌套示例见多风格提醒框。本站不转换 > [!NOTE] 等引用式提醒语法。

文章自动提醒

文章设置 wip: true 时,在标题信息后显示施工提醒。最后维护日期优先取 update,没有时取 publish;满 365 个上海自然日后显示内容时效提醒。

提醒不需要在正文手写组件。站点配置 site.article.notices 可关闭施工提醒、调整过期天数,或用 staleAfterDays: null 关闭过期提醒。wip 不会隐藏文章,也不用于控制访问权限。

常见问题

为什么组件没有显示?

先检查组件文件是否存在、名称是否一致,以及是否已全局注册。本站已提供 Alert、CodeGroup、Github、Bilibili 和 Youtube。本文中的 ExamplePanel 和 ExampleList 仅用于教学,需要自行实现后才能使用。

为什么示例里的插槽变成标题?

确认 #footer 位于组件开始和结束标记之间。普通 Markdown 区域中的 # footer 会被识别为一级标题。

为什么要用四个反引号展示源码?

当示例本身包含三个反引号的代码围栏时,外层使用四个反引号,才能把整个片段当作文本展示。写入实际文章时,只复制内层需要的部分。

如何接入自定义组件?

自定义组件需要按 Nuxt Content / Vue 的接口实现并注册。多风格提醒框、图片画廊和仓库卡片的状态见 Markdown 扩展功能。