.Qmd 和 .Md 的语法差异
.qmd 是 Quarto 使用的 Markdown 文件。普通 .md 主要负责排版文本,.qmd 在这个基础上加了可执行代码、输出控制、交叉引用、提示框、短代码等能力。
Quarto 底层用的是 Pandoc Markdown,所以大部分 Markdown 写法都能继续用,比如标题、列表、表格、脚注、公式。真正拉开差距的是下面这些语法。
Quarto 官方文档: Quarto Guide
1. YAML 不只写元信息
普通 .md 里的 front matter 多半只放标题、日期、标签。.qmd 的 YAML 还会控制渲染方式、输出格式和代码执行行为。
---
title: "Quarto 示例"
format:
html:
toc: true
code-fold: true
pdf:
documentclass: report
execute:
echo: true
warning: false
jupyter: python3
---这里的 format 决定输出成什么,execute 控制代码块怎么跑。jupyter: python3 会指定 Jupyter kernel。
同一个 .qmd 可以渲染成 HTML、PDF、Word、Reveal.js 幻灯片。普通 .md 通常只是一份静态文本,最多交给外部工具转换。
2. 代码块可以执行
.md 里的代码块大多只是展示。.qmd 的代码块可以真的执行,渲染时把结果塞回文档。
```{python}
import pandas as pd
df = pd.DataFrame({"name": ["Alice", "Bob"], "score": [95, 88]})
df
```如果是 R:
```{r}
summary(cars)
```Quarto 会根据代码块自动绑定执行引擎:发现 {r} 通常走 knitr,发现 {python}、{julia}、{bash} 等一般走 Jupyter。也可以手动指定:
---
engine: jupyter
---不想执行,只想展示 Quarto 代码块语法,可以用双花括号:
```{{python}}
print("只展示,不执行")
```3. #| 写代码块选项
.qmd 里最常见的特殊语法是 #|。它放在可执行代码块顶部,用来控制当前 cell 的行为。
```{python}
#| label: fig-sales
#| fig-cap: "月销售额趋势"
#| echo: false
#| warning: false
import matplotlib.pyplot as plt
plt.plot([12, 18, 15, 22])
plt.show()
```几个常用选项:
| 选项 | 用途 |
|---|---|
echo |
是否显示源代码 |
eval |
是否执行代码 |
output |
是否显示执行结果,也可以设为 asis 输出原始 Markdown |
warning |
是否显示 warning |
error |
是否把 error 写进文档 |
include |
是否包含代码和结果 |
label |
给代码单元命名,方便引用和调试 |
fig-cap |
图表标题 |
tbl-cap |
表格标题 |
文档级默认值可以写在 YAML 里,单个代码块再用 #| 覆盖:
---
execute:
echo: false
warning: false
---4. 行内代码也能执行
普通 Markdown 的行内代码只是等宽文本,比如 `code`。.qmd 可以在正文里执行表达式。
```{python}
radius = 5
```
圆的半径是 `{python} radius`。渲染后会变成:
圆的半径是 5。R 也是类似写法:
圆的半径是 `{r} radius`。这适合把计算结果写进叙述文本。最好提前在代码块里算好变量,行内表达式只取值,别在里面塞复杂逻辑。
5. Callout 提示框
.qmd 支持 callout,用 ::: 包起来。它比普通引用块更适合写提示、警告、补充说明、容易踩坑的地方。
::: {.callout-note}
这是一段普通提示。
:::
::: {.callout-tip title="写作习惯"}
同一份说明可以在 HTML 和 PDF 里保持类似的提示框样式。
:::
::: {.callout-caution collapse="true"}
### 展开后查看
这里放比较长的说明,默认折叠。
:::Quarto 内置的类型有 note、tip、warning、caution、important。
外观可以在 YAML 里调:
---
callout-appearance: simple
callout-icon: false
---6. Panel tabset 选项卡
写教程时经常要同时给不同语言或不同系统的命令。.qmd 可以用 .panel-tabset 做选项卡。
::: {.panel-tabset}
### Python
```python
print("hello")
```
### R
```r
print("hello")
```
### Bash
```bash
echo "hello"
```
:::渲染成 HTML 后,读者可以点选不同 tab。普通 .md 也能写多个标题,但没有这种内置交互结构。
7. 交叉引用
Quarto 的交叉引用比普通 Markdown 强很多。图、表、代码清单、公式、章节都可以自动编号,然后用 @id 引用。
图片引用:
{#fig-demo}
见 @fig-demo。表格引用:
| name | score |
| ----- | ----: |
| Alice | 95 |
| Bob | 88 |
: 成绩表 {#tbl-score}
见 @tbl-score。公式引用:
欧拉公式见 @eq-euler。
$$
e^{i\pi} + 1 = 0
$$ {#eq-euler}章节引用需要给标题加 #sec- ID,并开启章节编号:
---
number-sections: true
---### 数据处理 {#sec-data}
见 @sec-data。ID 前缀要对。图用 fig-,表用 tbl-,公式用 eq-,章节用 sec-。这些前缀是 Quarto 用来判断引用类型的。
8. 图表输出可以直接编号
可执行代码生成的图也能编号。只要给 cell 加 label 和 fig-cap。
```{python}
#| label: fig-line
#| fig-cap: "折线图"
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [1, 4, 9])
plt.show()
```
见 @fig-line。表格也一样:
```{r}
#| label: tbl-cars
#| tbl-cap: "cars 数据集前几行"
knitr::kable(head(cars))
```
见 @tbl-cars。这里的 label 要以 fig- 或 tbl- 开头,否则 Quarto 不会把它当成可引用对象。
9. 图示代码块
如果要画流程图、时序图、状态图、甘特图这类图示,.qmd 可以直接写 Mermaid 和 Graphviz。普通 .md 能不能渲染这些图,取决于平台;Quarto 把它们当成可执行 cell 处理。
官方文档: Diagrams
I. Mermaid
Mermaid 用 {mermaid} 代码块。它的 cell 选项不是 #|,而是 %%|。选项必须紧跟在代码块开头下面。
如下方 @fig-flow 所示,部署流程直接写在文档里。
```{mermaid}
%%| label: fig-flow
%%| fig-cap: "自动化部署流程"
flowchart LR
A[提交代码] --> B{CI 检查}
B -- 通过 --> C[构建镜像]
B -- 失败 --> D[通知开发者]
C --> E[部署]
```这里的 label 以 fig- 开头,所以可以用 @fig-flow 交叉引用。fig-cap 会变成图注,编号由 Quarto 处理。
时序图也是同样的写法:
```{mermaid}
%%| label: fig-seq
%%| fig-cap: "登录请求时序"
sequenceDiagram
participant Browser
participant API
participant DB
Browser->>API: POST /login
API->>DB: 查询用户
DB-->>API: 返回结果
API-->>Browser: 返回 token
```II. Graphviz
Graphviz 用 {dot} 代码块,适合画拓扑和依赖关系,也适合复杂网络结构。它的 cell 选项写成 //|。
```{dot}
//| label: fig-service-map
//| fig-cap: "服务依赖关系"
digraph G {
rankdir=LR;
node [shape=box];
"用户" -> "网关";
"网关" -> "服务 A";
"网关" -> "服务 B";
"服务 A" -> "Redis";
"服务 B" -> "PostgreSQL";
}
```
见 @fig-service-map。Mermaid 更像写流程说明,语法短,适合教程和架构草图。Graphviz 对图布局的控制更细,复杂关系图会更稳。
III. PlantUML
PlantUML 不属于 Quarto 原生 diagram 支持。要在 .qmd 里直接写 PlantUML,可以用 pandoc-ext/diagram 这个 Lua filter。
项目地址: pandoc-ext/diagram
先安装 Quarto extension:
quarto install extension pandoc-ext/diagram然后在 YAML 里启用 filter:
---
filters:
- diagram
---这个 filter 会调用外部程序生成图片。PlantUML 需要系统里能运行 plantuml 命令;常见安装方式还需要 Java。找不到命令时,可以用环境变量指定路径:
export PLANTUML_BIN=/usr/local/bin/plantuml也可以在 YAML 里写死路径:
---
filters:
- diagram
diagram:
engine:
plantuml:
execpath: /usr/local/bin/plantuml
---PlantUML 的代码块写成 {plantuml}。这里的 cell 选项用 PlantUML 注释语法,所以是 '|,不是 #|。
如 @fig-login-puml 所示,这是用 PlantUML 画的登录流程。
```{plantuml}
'| label: fig-login-puml
'| fig-cap: "用户登录时序图"
@startuml
actor User
participant "Web Portal" as Web
database "Auth DB" as DB
User -> Web: 输入凭证
Web -> DB: 验证用户
DB --> Web: 返回结果
Web --> User: 登录成功
@enduml
```类图也适合用 PlantUML:
```{plantuml}
'| label: fig-class-puml
'| fig-cap: "用户模型类图"
@startuml
package "Domain" {
class User {
- username: String
- passwordHash: String
+ login(): boolean
}
class Profile {
- avatar: String
- bio: String
}
User "1" *-- "1" Profile
}
@enduml
```pandoc-ext/diagram 生成的图也能走 Quarto 的图注和交叉引用。上面的 label 以 fig- 开头,所以正文里可以写 @fig-login-puml。
默认输出会按目标格式选择,常见的是 SVG 或 PNG,也可能是 PDF。如果某个格式在环境里跑不通,可以在 mime-type 里关掉:
---
filters:
- diagram
diagram:
engine:
plantuml:
mime-type:
application/pdf: false
---这个 filter 会执行本机命令,别拿来处理不可信的 .qmd。仓库 README 也专门提醒了这个安全问题。
Mermaid 和 Graphviz 是 Quarto 原生支持,配置轻。PlantUML 要多装 filter 和命令行工具,但它更适合写 UML 建模,比如类图和用例图。
IV. 外部文件
图很长时,不一定要把所有内容塞进 .qmd。可以把 Mermaid 放到 .mmd,或把 Graphviz 放到 .dot,再用 file 引入。
```{dot}
//| label: fig-kernel
//| fig-cap: "Linux kernel 结构图"
//| file: linux-kernel-diagram.dot
```Mermaid 也可以这样写:
```{mermaid}
%%| label: fig-state
%%| fig-cap: "订单状态机"
%%| file: order-state.mmd
```这种写法适合复杂图。.qmd 负责叙述和引用,图示源码单独维护。
V. 尺寸和输出格式
默认情况下,Quarto 会按图示的自然尺寸渲染。HTML 输出里图示会自动响应页面宽度,通常不会撑破正文栏。
需要指定大小时,用 fig-width 和 fig-height:
```{mermaid}
%%| fig-width: 6.5
flowchart LR
A[开始] --> B[处理]
B --> C[结束]
```如果不想让 HTML 图示响应式缩放,可以关掉:
---
fig-responsive: false
---Mermaid 在不同输出格式里的处理方式不一样。HTML 通常用 Mermaid 的 JavaScript 渲染,gfm 会保留 Mermaid 代码块。PDF、docx 这类打印格式会生成 PNG。打印格式渲染图示时会用 Chrome 或 Edge,没有浏览器环境时可以安装 Quarto 的 headless shell:
quarto install chrome-headless-shell也可以手动指定 Mermaid 输出格式:
---
format:
html:
mermaid-format: svg
---常用值有 js 和 png,也可以选 svg。如果目标是 LaTeX 生成的 PDF,svg 可能需要额外工具,默认的 png 更省事。
VI. 主题和源码显示
Mermaid 可以跟随 Quarto 的主题,也可以单独指定 Mermaid 内置主题:
---
format:
html:
mermaid:
theme: forest
---Mermaid 内置主题包括 default、dark、forest、neutral。
图示 cell 默认不在结果里显示源码。如果写教程时想同时展示源码和图,可以打开 echo:
```{mermaid}
%%| echo: true
flowchart LR
A --> B
```
```{dot}
//| echo: true
graph G {
A -- B
}
```这部分是 .qmd 比普通 .md 更完整的地方:图示不只是代码块,还能参与编号、引用、输出格式转换和主题控制。配合 pandoc-ext/diagram 这类 filter,还能把 PlantUML 接进同一套写作流程。
10. 文献引用
.qmd 可以直接用 Pandoc 的 citation 语法处理参考文献。最常见的是在 YAML 里指定 bibliography,正文里用 @key 或 [@key] 引用。
官方文档: Citations
先准备一个 BibTeX 文件,比如 references.bib:
@book{knuth1984,
author = {Donald E. Knuth},
title = {The TeXbook},
year = {1984},
publisher = {Addison-Wesley}
}
@book{wickham2015,
author = {Hadley Wickham},
title = {R Packages},
year = {2015},
publisher = {O'Reilly Media}
}然后在文档头部挂上它:
---
title: "Quarto 引用示例"
bibliography: references.bib
---正文里可以这样写:
这是括号引用 [@knuth1984]。
这是带定位信息的写法 [@knuth1984, pp. 33-35]。
也可以一次引多个条目 [@knuth1984; @wickham2015]。
@knuth1984 这类写法会直接把作者名带进正文。如果不手动指定位置,Quarto 会把参考文献列表放到文末。想自己控制位置,可以放一个 refs 容器:
### References
::: {#refs}
:::样式默认走 Chicago author-date。要改成别的格式,比如 Nature、APA,可以加 CSL 文件:
---
bibliography: references.bib
csl: nature.csl
---有时候正文不想引用某条文献,但又想把它放进参考文献列表,可以用 nocite:
---
bibliography: references.bib
nocite: |
@knuth1984
---想把 .bib 里的所有条目都列出来也行:
---
bibliography: references.bib
nocite: |
@*
---这套东西普通 .md 一般做不到,除非外部平台自己再接一层 Pandoc 或别的文献处理器。
11. Div 和 Span 属性
.qmd 继承了 Pandoc Markdown 的属性语法,可以给块级内容或行内内容加 class、id、属性。
块级内容:
::: {#example .border}
这段内容有一个 ID 和一个 class。
:::行内内容:
[这几个字会被标记]{.mark}这个语法在普通 Markdown 渲染器里不一定能用。Quarto 会把它交给 Pandoc 处理,再根据输出格式生成对应结果。
属性顺序也有要求:先写 ID,再写 class,最后写键值对。
[正确]{#id .class key="value"}
[不稳]{.class key="value" #id}11. Shortcode
Quarto 有自己的 shortcode,形式和 Hugo 有点像:
{{< meta title >}}
{{< var version >}}
{{< pagebreak >}}
{{< kbd Ctrl-C >}}
{{< video https://www.youtube.com/embed/xxx >}}常用的几个:
| shortcode | 用途 |
|---|---|
meta |
读取文档 metadata |
var |
读取 _variables.yml 里的变量 |
env |
读取环境变量 |
include |
插入另一个文件内容 |
embed |
嵌入 notebook 里的 cell |
pagebreak |
插入分页 |
kbd |
显示快捷键 |
video |
插入视频 |
如果文章就是在讲 shortcode,代码块里可能会被 Quarto 误处理。可以用 shortcodes=false:
```{shortcodes=false}
{{< var version >}}
```或者加一层花括号转义:
{{{< var version >}}}12. include 复用内容
.qmd 可以用 include 把另一个文件插进来,适合复用说明、准备代码、统一声明。
{{< include _intro.qmd >}}被 include 的文件通常用下划线开头,比如 _intro.qmd。这样项目渲染时不会把它当成独立页面。
也可以把脚本内容塞进代码块:
```python
{{< include _demo.py >}}
```有两个细节容易踩:
include要单独占一行,前后留空行。- include 近似于复制粘贴。被插入文件里的相对路径,会按主文件位置解析。
13. 原始内容块
.qmd 可以直接写某个输出格式的原始内容。比如 HTML:
```{=html}
<iframe src="https://quarto.org/" width="600" height="400"></iframe>
```LaTeX:
```{=latex}
\newpage
```这类内容只对对应格式生效。写多格式文档时要小心,HTML 片段放到 PDF 输出里通常没意义。
14. 扩展和过滤器
Quarto 可以挂 Lua filter 或 extension。它们通常写在 YAML 里:
---
filters:
- diagram
---扩展可以改 AST、增加输出能力、封装 shortcode。普通 .md 本身没有这种项目级扩展机制,除非外部构建系统另行处理。