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,因此不依赖额外组件。
源码
::div
这里是容器内的 **Markdown 内容**。
- 标题来自文章 Meta。
- 正文继续使用 Markdown。
::
效果
这里是容器内的 Markdown 内容。
- 标题来自文章 Meta。
- 正文继续使用 Markdown。
如果有一个已注册的 ExamplePanel.vue,可以用 ::example-panel 调用它。这个名称只是下文的教学示例,当前项目没有安装该组件。
行内语法与属性
行内组件使用一个冒号,例如 :span[文字],在前面保留空格以分隔正文。仅需给一段文字添加属性时,可以使用 [文字]{属性}。
源码
当前示例: :span[已校对]。
[把指针放在这段文字上]{title="这是通过 MDC 设置的 title 属性"}。
效果
当前示例: 已校对。
把指针放在这段文字上。
链接、图片、强调和行内代码也可以带属性。以下链接通过 target 指定在新标签页打开。
[Nuxt Content](https://content.nuxt.com){target="_blank"}
{.类名 #标识符} 可以指定 class 和 id;class 对应的样式仍须由站点提供,不能仅凭名称产生效果。
给组件传递 Props
Props 是组件接收的参数。以下示例都假设已经注册了 ExamplePanel,只展示源代码。
行内参数
参数少时,可以直接放在花括号中:
::example-panel{title="环境要求"}
请先确认项目的运行环境。
::
字符串使用引号包裹。数值、布尔值、数组和对象应使用组件声明的类型;复杂数据可以使用 YAML 参数块,或使用冒号前缀传入 JSON。
YAML 参数块
参数较多时,将它们写在组件开头的两行 --- 之间。这一块只属于当前组件,与文件顶部的文章 Meta 分开。
::example-panel
---
title: 环境要求
---
请先确认项目的运行环境。
::
数组与对象
下面用假设的 ExampleList 展示结构化参数。它需要声明并处理相应的 items 和 options Props。
::example-list{:items='["Markdown", "MDC"]' :options='{"compact": true}'}
::
带冒号的属性可以解析 JSON,因此外层用单引号,JSON 字符串内部保留双引号。属性值的最终类型应与组件定义一致。
默认插槽与具名插槽
默认插槽承载组件内的主要内容;具名插槽用 #插槽名 切分不同区域。插槽标记属于组件内部,不是普通章节标题。
下面的 ExamplePanel 使用默认插槽和 footer 插槽:
::example-panel{title="发布检查"}
请确认图片和链接可以正常访问。
#footer
[阅读 Markdown 教程](/posts/markdown/markdown)
::
若要自行接入这个教学组件,可以创建 apps/website/app/components/content/ExamplePanel.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 文档。
容器嵌套
嵌套时,外层使用更多冒号,让每个结束标记对应到正确的层级。
源码
:::div
这是外层内容。
::div
这是内层内容,可以继续使用 **强调**。
::
这是回到外层后的内容。
:::
效果
这是外层内容。
这是内层内容,可以继续使用 强调。
这是回到外层后的内容。
同样的结构也适用于已经注册的 Vue 组件。每一层都应正确闭合,避免后续章节被误收进容器。
绑定文章数据
使用双花括号可以引用当前文章的数据。$doc 表示渲染时可访问的文档数据。
源码
本文标题:{{ $doc.title }}
发布日期:{{ $doc.publish }}
效果
本文标题:MDC 教程
发布日期:1970-01-01
Props 也可以绑定文档数据。以下写法需要先接入上文的 ExamplePanel:
::example-panel{:title="title"}
这里的标题取自文章的 title 字段。
::
如果要查询新的自定义字段,需要扩展内容集合的 schema 和校验规则。数据绑定不等于在 Markdown 中编写完整 Vue 模板;业务逻辑应放在组件中。
Nuxt Content 代码语法
语言、文件名与行号标记
代码围栏可以携带语言、文件名和需要强调的行号。这些内容会被解析成代码组件的参数,详见 Nuxt Content 的 ProsePre 文档。
源码
```ts [article.ts]{2}
const title = "MDC 教程";
const published = true;
const tags = ["MDC", "Nuxt Content"];
```
当前效果
const title = "MDC 教程";
const published = true;
const tags = ["MDC", "Nuxt Content"];
| 标记 | 传递的信息 | 本站当前效果 |
|---|---|---|
ts | 代码语言 | 用于语法着色 |
[article.ts] | 文件名 | 显示为代码块标题或代码组标签 |
{2} | 第二行的强调标记 | 显示强调背景与侧边标记 |
本站代码块已显示文件名、行强调和复制按钮;没有文件名时显示语言名称。复制保留原始缩进和换行,失败时会提示手动选择代码。语法高亮随站点深浅主题切换。
代码组
使用 ::code-group 包住多个代码块。文件名标记作为标签名称,没有文件名时使用语言名称。
源码
::code-group
```bash [安装依赖]
vp install
```
```bash [运行检查]
vp run check
```
::
效果
vp install
vp run check
方向键切换标签,Home / End 移动到首尾。复制按钮只复制当前面板,各个代码组独立切换。切换标签后,原面板的横向滚动位置保留。
下面第二个代码组展示同一条配置的两种写法,可用来核对各组状态互不影响。
const notices = {
staleAfterDays: 365,
};
notices:
staleAfterDays: 365
图片展示与预览
标准 Markdown 图片自动支持预览,可填写尺寸和可选图注。提供宽高可预留展示空间,减轻图片加载后的布局变化。
源码
{width="1200" height="675" caption="Markdown 经 Nuxt Content 解析后渲染为文章页面。"}
效果
Markdown 经 Nuxt Content 解析后渲染为文章页面。
点击图片打开预览,滚轮或双指手势缩放,放大后可拖拽。工具栏提供放大、缩小、复位和关闭;键盘使用加减号、方向键、0 和 Escape。缩放范围为初始适应尺寸的 1–4 倍。
通过布尔参数 preview 关闭单张图片的预览:
{:preview="false"}
图片被链接包裹时,继续执行链接跳转,不打开预览:
[](/posts/markdown/markdown)
封面和头像不受此功能影响。图片网格、前后切图和作品卡片尚未提供。
GitHub 仓库卡片
使用公开仓库的 owner/name 标识。以下源码可直接复制,结束标记 :: 必须保留;卡片不接收插槽内容。
源码
::github{repo="nuxt/content"}
::
效果
卡片显示头像、仓库名称、简介、Star、Fork、许可证和主要语言,点击整张卡片在新标签页打开 GitHub。文章与关于页均可使用,普通 GitHub 链接不会自动转换。
仓库信息在站点构建时生成快照,随下一次构建更新;阅读页面时不请求 GitHub API。未取得的字段显示 -,实际计数为零时显示 0。头像加载失败时显示本地图标。
视频卡片
使用 Bilibili 与 Youtube 组件嵌入视频。卡片支持懒加载、全屏与响应式宽度,默认不自动播放:
::bilibili{bvid="BV1fK4y1s7Qf" title="Bilibili 视频示例"}
::
::youtube{id="5gIf0_xpFPI" title="YouTube 视频示例"}
::
也可通过 src 传入视频链接。完整效果、分 P 与开始时间参数见 在文章中嵌入视频。
正文提醒框
使用 ::alert 展示说明、建议或警告。正文支持 Markdown;标题是纯文本,省略时显示对应类型的默认标题。
源码
::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 扩展功能。