跳转到内容

@astrojs/ markdoc

Astro 集成 支持使用 Markdoc 创建组件、页面和内容集合条目。

Markdoc 允许你使用 Astro 组件增强 Markdown。如果你有现有的 Markdoc 内容,此集成允许你使用内容集合将这些文件引入到 Astro 项目中。

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

在新的终端窗口中运行以下命令之一。

终端窗口
npx astro add markdoc

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

首先,安装 @astrojs/markdoc

终端窗口
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,有一个官方的 Markdoc 语言扩展,它包含配置标签的语法高亮和自动补全功能。有关更多信息,请参阅 GitHub 上的语言服务器

要设置此扩展,请在项目根目录中创建一个 markdoc.config.json 文件,并包含以下内容:

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

markdoc.config.mjs 设置为你的配置文件,其中包含 schema 对象,并使用 path 属性定义 Markdoc 文件的存储位置。由于 Markdoc 专门用于内容集合,你可以使用 src/content

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

  • 目录src/
    • 目录content/
      • 目录docs/
        • why-markdoc.mdoc
        • quick-start.mdoc

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

src/pages/why-markdoc.astro
---
import { getEntry, render } from 'astro:content';
const entry = await getEntry('docs', 'why-markdoc');
const { Content } = await render(entry);
---
<!--Access frontmatter properties with `data`-->
<h1>{entry.data.title}</h1>
<!--Render Markdoc contents with the Content component-->
<Content />
有关更多信息,请参阅Astro 内容集合文档

你可能需要向内容传递变量。这在传递 SSR 参数(如 A/B 测试)时很有用。

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

src/pages/why-markdoc.astro
---
import { getEntry, render } from 'astro:content';
const entry = await getEntry('docs', 'why-markdoc');
const { Content } = await render(entry);
---
<!--Pass the `abTest` param as a variable-->
<Content abTestGroup={Astro.params.abTestGroup} />

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

src/content/docs/why-markdoc.mdoc
{% if $abTestGroup === 'image-optimization-lover' %}
Let me tell you about image optimization...
{% /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',
},
});

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

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

现在可以在 Markdoc 中以 $frontmatter 的形式访问它。

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

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

此示例渲染一个 Aside 组件,并允许将 type prop 作为字符串传递

markdoc.config.mjs
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
tags: {
aside: {
render: component('./src/components/Aside.astro'),
attributes: {
// Markdoc requires type defs for each attribute.
// These should mirror the `Props` type of the component
// you are rendering.
// See Markdoc's documentation on defining attributes
// https://markdoc.dev/docs/attributes#defining-attributes
type: { type: String },
},
},
},
});

现在,此组件可以在你的 Markdoc 文件中使用 {% aside %} 标签。子组件将传递到组件的默认插槽中。

# Welcome to Markdoc 👋
{% aside type="tip" %}
Use tags like this fancy "aside" to add some _flair_ to your docs.
{% /aside %}

标签和节点仅限于 .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 prop 传递给配置中的任何标签节点

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 },
},
},
},
});

从 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';

Markdoc {% partial /%} 标签允许你在 Markdoc 内容中渲染其他 .mdoc 文件。

这对于在多个文档中重用内容很有用,并且允许你拥有不遵循集合模式的 .mdoc 内容文件。

此示例展示了一个 Markdoc 页脚部分,可用于博客集合条目中

src/content/blog/_footer.mdoc
Social links:
- [Twitter / X](https://twitter.com/astrodotbuild)
- [Discord](https://astro.js.cn/chat)
- [GitHub](https://github.com/withastro/astro)

使用 {% partial /%} 标签在博客文章底部渲染页脚。使用文件路径应用 file 属性,可以使用相对路径或导入别名。

src/content/blog/post.mdoc
# My Blog Post
{% partial file="./_footer.mdoc" /%}

@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({
// Choose from Shiki's built-in themes (or add your own)
// Default: 'github-dark'
// https://shiki.style/themes
theme: 'dracula',
// Enable word wrap to prevent horizontal scrolling
// Default: false
wrap: true,
// Pass custom languages
// Note: Shiki has countless langs built-in, including `.astro`!
// https://shiki.style/languages
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 样式表,请参阅我们的语法高亮指南

你可能希望将标准 Markdown 元素(例如段落和粗体文本)渲染为 Astro 组件。为此,你可以配置一个 Markdoc 节点。如果给定节点接收到属性,它们将作为组件 prop 可用。

此示例使用自定义 Quote.astro 组件渲染块引用

markdoc.config.mjs
import { defineMarkdocConfig, nodes, component } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
blockquote: {
...nodes.blockquote, // Apply Markdoc's defaults for other options
render: component('./src/components/Quote.astro'),
},
},
});
参阅Markdoc 节点文档以了解所有内置节点和属性。

@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, // Preserve default anchor link generation
render: component('./src/components/Heading.astro'),
},
},
});

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

  • level: number 标题级别 1 - 6
  • id: string 从标题文本内容生成的 id。这对应于由内容 render() 函数生成的 slug

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

Astro 的 <Image /> 组件不能直接在 Markdoc 中使用。但是,你可以配置一个 Astro 组件来覆盖默认图像节点(每次使用原生 ![]() 图像语法时),或者将其作为一个自定义 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, // Apply Markdoc's defaults for other options
    render: component('./src/components/MarkdocImage.astro'),
    },
    },
    });
  4. 任何 .mdoc 文件中的原生图像语法现在都将使用 <Image /> 组件来优化你的本地图像。远程图像仍然可以使用,但不会由 Astro 的 <Image /> 组件渲染。

    src/content/blog/post.mdoc
    <!-- Optimized by <Image /> -->
    ![A picture of a cat](/cat.jpg)
    <!-- Unoptimized <img> -->
    ![A picture of a dog](https://example.com/dog.jpg)

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

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

  1. 创建一个 MarkdocFigure.astro 组件,以接收必要的 props 并渲染带有标题的图像

    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: {
    width: {
    type: String,
    },
    height: {
    type: String,
    },
    caption: {
    type: String,
    },
    ...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!" /%}

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 文档

Markdoc 默认使用 <article> 标签包装文档。这可以通过 document Markdoc 节点进行更改。它接受 HTML 元素名称或 null(如果你希望移除包装器元素)

markdoc.config.mjs
import { defineMarkdocConfig, nodes } from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
document: {
...nodes.document, // Apply defaults for other options
render: null, // default 'article'
},
},
});

Astro Markdoc 集成处理 Markdoc 选项和功能的配置,这些选项和功能无法通过 markdoc.config.js 文件获得。

启用与 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 %}

更多集成

前端框架

适配器

其他集成

贡献 社区 赞助