跳转到内容

@astrojs/ markdoc

这个 Astro 集成 允许使用 Markdoc 来创建组件、页面和内容集合条目。

Markdoc 允许你使用 Astro 组件 增强 Markdown。如果你已经使用 Markdoc 编写了现有内容,此集成允许你使用内容集合将这些文件带到 Astro 项目中。

Astro 包含了一个 astro add 命令,用于自动设置官方集成。如果你愿意,可以改为手动安装集成

在新的终端窗口中运行下面其中一个命令。

Terminal window
npx astro add markdoc

如果你遇到任何问题,请随时在 GitHub 上向我们报告,并尝试下面的手动安装步骤。

首先,安装 @astrojs/markdoc 包:

Terminal window
npm install @astrojs/markdoc

然后,使用 integrations 属性将集成应用到你的 astro.config.* 文件中:

astro.config.mjs
import { defineConfig } from 'astro/config';
import markdoc from '@astrojs/markdoc';
export default defineConfig({
// ...
integrations: [markdoc()],
});

VS Code 默认支持 Markdown。但是,为了支持 Markdoc 编辑器,你可能希望在 VSCode 配置中添加以下设置。这样可以确保编写 Markdoc 文件时提供类似 Markdown 的编辑器体验。

.vscode/settings.json
{
"files.associations": {
"*.mdoc": "markdown"
}
}

Markdoc 文件只能在内容集合中使用。使用 .mdoc 扩展名将条目添加到任何内容集合中:

  • 文件夹src/
    • 文件夹content/
      • 文件夹docs/
        • why-markdoc.mdoc
        • quick-start.mdoc

然后,使用 内容集合 API 查询你的集合:

src/pages/why-markdoc.astro
---
import { getEntryBySlug } from 'astro:content';
const entry = await getEntryBySlug('docs', 'why-markdoc');
const { Content } = await entry.render();
---
<!--使用 `data` 访问 frontmatter 属性`-->
<h1>{entry.data.title}</h1>
<!--使用 Content 组件渲染 Markdoc 内容-->
<Content />
了解更多关于 Astro 内容集合文档 的信息。

@astrojs/markdoc 提供了配置选项,以使用 Markdoc 的所有功能,并将 UI 组件连接到你的内容。

将 Astro 组件作为 Markdoc 标签

段落标题 将 Astro 组件作为 Markdoc 标签

你可以配置 Markdoc 标签,将其映射到 .astro 组件。你可以通过在项目根目录创建 markdoc.config.mjs|ts 文件并配置 tag 属性来添加新的标签。

这个例子渲染了一个 Aside 组件,并允许传递一个 type 字符串属性:

markdoc.config.mjs
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
aside: {
render: component('./src/components/Aside.astro'),
attributes: {
// Markdoc 要求每个属性都有类型定义。
// 这些应该反映你正在渲染的组件的 `Props` 类型
// 查看 Markdoc 的属性定义文档:
// https://markdoc.dev/docs/attributes#defining-attributes
type: { type: String },
},
},
},
});

现在,这个组件可以在你的 Markdoc 文件中使用 {% aside %} 标签。子元素将被传递给你的组件的默认插槽:

# 欢迎来到 Markdoc 👋
{% aside type="tip" %}
使用像这种花哨的“aside”标签为你的文档添加一些 _花活_
{% /aside %}

使用 npm 包和 TypeScript 文件中的 Astro 组件

段落标题 使用 npm 包和 TypeScript 文件中的 Astro 组件

你可能需要在 TypeScript 或 JavaScript 文件中使用 Astro 组件。当使用 npm 包和设计系统时,这是很常见的。

你可以将导入名称作为 component() 函数的第二个参数:

markdoc.config.mjs
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
tabs: {
render: component('@astrojs/starlight/components', 'Tabs'),
},
},
});

这将在内部生成以下导入语句:

import { Tabs } from '@astrojs/starlight/components';

@astrojs/markdoc 会自动为你的标题添加锚点链接,并且通过 内容集合 API 生成一个 headings 列表。为了进一步自定义标题的渲染方式,你可以将 Astro 组件 作为 Markdoc 节点 应用。

此示例使用 render 属性渲染 Heading.astro 组件:

markdoc.config.mjs
import { defineMarkdocConfig, nodes, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
heading: {
...nodes.heading, // 保留默认锚点链接生成
render: component('./src/components/Heading.astro'),
},
},
});

所有的 Markdown 标题都会渲染 Heading.astro 组件,并将以下 attributes 作为组件的 props 传递:

例如,标题 ### Level 3 heading! 将会传递 level: 3id: 'level-3-heading' 作为组件的 props。

@astrojs/markdoc 提供了 ShikiPrism 扩展来高亮你的代码块。

使用 extends 属性将 shiki() 扩展应用到你的 Markdoc 配置中,你可以选择传递一个 shiki 配置对象:

markdoc.config.mjs
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
import shiki from '@astrojs/markdoc/shiki';
export default defineMarkdocConfig({
extends: [
shiki({
// 从 Shiki 的内置主题中进行选择(或添加你自己的主题)
// 默认:'github-dark'
// https://github.com/shikijs/shiki/blob/main/docs/themes.md
theme: 'dracula',
// 启用单词折叠以防止水平滚动
// 默认:false
wrap: true,
// 传入自定义语言
// 注意:Shiki 内置了许多的 langs,包括 `.astro`!
// https://github.com/shikijs/shiki/blob/main/docs/languages.md
langs: [],
}),
],
});

使用 extends 属性将 prism() 扩展应用到你的 Markdoc 配置中。

markdoc.config.mjs
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
import prism from '@astrojs/markdoc/prism';
export default defineMarkdocConfig({
extends: [prism()],
});
要了解如何配置 Prism 样式表,请看我们的语法高亮指南

默认情况下,Markdoc 会使用 <article> 标签包装文档。这可以从 document Markdoc 节点中更改。这个节点接受一个 HTML 元素名称,或者如果你想删除包装元素的话,可以传递 null

markdoc.config.mjs
import { defineMarkdocConfig, nodes } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
document: {
...nodes.document, // 应用默认的其他配置项
render: null, // 默认 'article'
},
},
});

自定义 Markdoc 节点 / 元素

段落标题 自定义 Markdoc 节点 / 元素

你可能想要将标准的 Markdown 元素,例如段落和加粗文本,渲染为 Astro 组件。为此,你可以配置一个 Markdoc 节点。如果给定的节点接收到属性,它们可将作为组件 props 来使用。

这个例子使用一个自定义的 Quote.astro 组件渲染引用块:

markdoc.config.mjs
import { defineMarkdocConfig, nodes, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
blockquote: {
...nodes.blockquote, // 应用默认的其他配置项
render: component('./src/components/Quote.astro'),
},
},
});

在 Markdoc 中自定义图片组件

段落标题 在 Markdoc 中自定义图片组件

Astro 的 <Image /> 组件不能直接在 Markdoc 中使用。但是你可以配置一个 Astro 组件来覆盖每次使用原生 ![]() 图片语法时的默认图片节点,或者作为一个自定义的 Markdoc 标签让你可以指定额外的图片属性。

覆盖 Markdoc 的默认图片节点

段落标题 覆盖 Markdoc 的默认图片节点

为了覆盖默认图片节点,你可以配置一个 .astro 组件来代替标准的 <img> 渲染。

  1. 构建一个自定义的 MarkdocImage.astro 组件,将你的图片的必要的 srcalt 属性传递给 <Image /> 组件:

    src/components/MarkdocImage.astro
    ---
    import { Image } from "astro:assets";
    interface Props {
    src: ImageMetadata;
    alt: string;
    }
    const { src, alt } = Astro.props;
    ---
    <Image src={src} alt={alt} />
  2. <Image /> 组件需要为无法使用 ![]() 语法来提供的远程图片提供 widthheight 属性。为了避免在使用远程图片时出现错误,当发现远程 URL src 属性时,会更新你的组件并渲染成标准的 HTML <img> 标签:

    src/components/MarkdocImage.astro
    ---
    import { Image } from "astro:assets";
    interface Props {
    src: ImageMetadata | string;
    alt: string;
    }
    const { src, alt } = Astro.props;
    ---
    <Image src={src} alt={alt} />
    {
    typeof src === 'string' ? <img src={src} alt={alt} /> : <Image src={src} alt={alt} />
    }
  3. 配置 Markdoc 来覆盖默认的图片节点并渲染 MarkdocImage.astro

    markdoc.config.mjs
    import { defineMarkdocConfig, nodes, component } from '@astrojs/markdoc/config';
    export default defineMarkdocConfig({
    nodes: {
    image: {
    ...nodes.image, // 应用 Markdoc 的其他默认选项
    render: component('./src/components/MarkdocImage.astro'),
    },
    },
    });
  4. 现在,任何 .mdoc 文件中的原生图片语法都将使用 <Image /> 组件来优化你的本地图片。你仍然可以使用远程图片,但是它们将不会被 Astro 的 <Image /> 组件渲染。

    src/content/blog/post.mdoc
    <!-- 由 <Image /> 优化组件 -->
    ![A picture of a cat](/cat.jpg)
    <!-- 未优化的 <img> 标签 -->
    ![A picture of a dog](https://example.com/dog.jpg)

创建一个自定义的 Markdoc 图片标签

段落标题 创建一个自定义的 Markdoc 图片标签

Markdoc 的 image 标签允许你在图片上设置额外的属性,这是用 ![]() 语法无法实现的。例如,自定义图片标签允许你对需要 widthheight 的远程图片使用 Astro 的 <Image /> 组件。

以下步骤将创建一个自定义的 Markdoc 图片标签,以显示一个带有标题的 <figure> 元素,使用 Astro 的 <Image /> 组件来优化图片。

  1. 创建一个 MarkdocFigure.astro 组件,接收必要的属性并渲染一个带有标题的图片:

    src/components/MarkdocFigure.astro
    ---
    // src/components/MarkdocFigure.astro
    import { Image } from "astro:assets";
    interface Props {
    src: ImageMetadata | string;
    alt: string;
    width: number;
    height: number;
    caption: string;
    }
    const { src, alt, width, height, caption } = Astro.props;
    ---
    <figure>
    <Image {src} {alt} {width} {height} />
    {caption && <figcaption>{caption}</figcaption>}
    </figure>
  2. 配置你的自定义图片标签来渲染你的 Astro 组件:

    markdoc.config.mjs
    import { component, defineMarkdocConfig, nodes } from '@astrojs/markdoc/config';
    export default defineMarkdocConfig({
    tags: {
    image: {
    attributes: nodes.image.attributes,
    render: component('./src/components/MarkdocFigure.astro'),
    },
    },
    });
  3. 在 Markdoc 文件中使用 image 标签来显示一个带有标题的图形,为你的组件提供所有必要的属性:

    {% image src="./astro-logo.png" alt="Astro Logo" width="100" height="100" caption="a caption!" %}

标签和节点仅限于 .astro 文件。要在 Markdoc 中嵌入客户端 UI 组件,请 使用一个包装器 .astro 组件来渲染框架组件 和你想要的 client: 指令。

此示例使用 ClientAside.astro 组件包装 React Aside.tsx 组件:

src/components/ClientAside.astro
---
import Aside from './Aside';
---
<Aside {...Astro.props} client:load />

这个 Astro 组件现在可以传递给你的配置中的任何 标签节点render 属性:

markdoc.config.mjs
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
aside: {
render: component('./src/components/ClientAside.astro'),
attributes: {
type: { type: String },
},
},
},
});

markdoc.config.mjs|ts 文件接受 所有 Markdoc 配置选项,包括 标签函数

你可以从 markdoc.config.mjs|ts 文件的默认导出中传递这些选项:

markdoc.config.mjs
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
functions: {
getCountryEmoji: {
transform(parameters) {
const [country] = Object.values(parameters);
const countryToEmojiMap = {
japan: '🇯🇵',
spain: '🇪🇸',
france: '🇫🇷',
};
return countryToEmojiMap[country] ?? '🏳';
},
},
},
});

现在,你可以从任何 Markdoc 内容条目中调用这个函数:

¡Hola {% getCountryEmoji("spain") %}!
查看 Markdoc 文档 了解更多关于在你的内容中使用变量或函数的信息。

如果你使用的是 VS Code,有一个官方的 Markdoc 语言扩展,它包括了为配置的标签提供语法高亮和自动完成。在 GitHub 上查看语言服务器 的更多信息。

要设置这个扩展,创建一个 markdoc.config.json 文件到项目根目录,内容如下:

[
{
"id": "my-site",
"path": "src/content",
"schema": {
"path": "markdoc.config.mjs",
"type": "esm",
"property": "default",
"watch": true
}
}
]

schema 属性 包含了为 Astro 内容集合配置语言服务器的所有信息。它接受以下属性:

  • path: 配置文件的路径。
  • type: 配置文件使用的模块类型(esm 允许 import 语法)。
  • property: 包含配置对象的导出属性名称。
  • watch: 告诉服务器监听配置中的更改。 顶级 path 属性告诉服务器内容位于哪里。由于 Markdoc 是特定于内容集合的,因此可以使用 src/content

你可能需要将 变量 传递给你的内容。当传递 SSR 参数时,这是很有用的,比如 A/B 测试。

变量可以通过 Content 组件作为 props 传递:

src/pages/why-markdoc.astro
---
import { getEntryBySlug } from 'astro:content';
const entry = await getEntryBySlug('docs', 'why-markdoc');
const { Content } = await entry.render();
---
<!--将`abTest`参数作为变量传递-->
<Content abTestGroup={Astro.params.abTestGroup} />

现在,abTestGroup 可以作为变量在 docs/why-markdoc.mdoc 中使用:

src/content/docs/why-markdoc.mdoc
{% if $abTestGroup === 'image-optimization-lover' %}
让我告诉你关于图像优化...
{% /if %}

要使变量对所有 Markdoc 文件全局可用,你可以使用 markdoc.config.mjs|ts 中的 variables 属性:

markdoc.config.mjs
import { defineMarkdocConfig } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
variables: {
environment: process.env.IS_PROD ? 'prod' : 'dev',
},
});

从你的 Markdoc 内容中访问 frontmatter

段落标题 从你的 Markdoc 内容中访问 frontmatter

要访问 frontmatter,你可以在渲染内容的地方将条目 data 属性 作为变量 传递:

src/pages/why-markdoc.astro
---
import { getEntry } from 'astro:content';
const entry = await getEntry('docs', 'why-markdoc');
const { Content } = await entry.render();
---
<Content frontmatter={entry.data} />

这样就可以在你的 Markdoc 中作为 $frontmatter 访问。

Astro Markdoc 集成处理了在 markdoc.config.js 文件中不可用的 Markdoc 选项和功能的配置。

允许在 Markdoc 标签和节点旁边编写 HTML 标记。

默认情况下,Markdoc 不会将 HTML 标记识别为语义内容。

要实现更接近 Markdown 的体验,其中 HTML 元素可以与你的内容一起使用,请将 allowHTML:true 作为 markdoc 集成选项设置。这将在 Markdoc 标记中启用 HTML 解析。

astro.config.mjs
import { defineConfig } from 'astro/config';
import markdoc from '@astrojs/markdoc';
export default defineConfig({
// ...
integrations: [markdoc({ allowHTML: true })],
});

默认情况下,任何使用四个空格缩进的内容都会被视为代码块。不幸的是,这种行为使得在具有复杂结构的文档中用任意级别的缩进来提高可读性变得困难。

在使用 Markdoc 的嵌套标签时,将标签内的内容缩进可以帮助清楚地显示层级深度。为了支持任意缩进,我们需要禁用基于缩进的代码块,并修改几个其他的 markdown-it 解析规则,以忽略缩进的代码块。即通过启用 ignoreIndentation 选项可以应用这些修改。

astro.config.mjs
import { defineConfig } from 'astro/config';
import markdoc from '@astrojs/markdoc';
export default defineConfig({
// ...
integrations: [markdoc({ ignoreIndentation: true })],
});
# Welcome to Markdoc with indented tags 👋
# Note: Can use either spaces or tabs for indentation
{% custom-tag %}
{% custom-tag %} ### Tags can be indented for better readability
{% another-custom-tag %}
This is easier to follow when there is a lot of nesting
{% /another-custom-tag %}
{% /custom-tag %}
{% /custom-tag %}

更多集成

UI 框架

SSR 适配器

其他集成