跳转到内容

在 Astro 中使用 Markdown

Markdown 通常用于编写像博客文章和文档这样文本量大的内容。Astro 内置了对 Markdown 文件的支持,这些文件还可以包含 frontmatter YAML(或 TOML)来定义自定义属性,如标题、描述和标签。

在 Astro 中,你可以使用 GitHub Flavored Markdown 编写内容,然后在 .astro 组件中渲染它。这结合了一种为内容设计的熟悉的写作格式与 Astro 组件语法和架构的灵活性。

你的本地 Markdown 文件可以存放在 src/ 目录下的任何位置。位于 src/pages/ 内的 Markdown 文件将自动在你的网站上生成Markdown 页面

你的 Markdown 内容和 frontmatter 属性可以通过本地文件导入在组件中使用,或者通过内容集合辅助函数获取数据并查询和渲染后使用

本地 Markdown 可以通过单个文件的 import 语句导入到 .astro 组件中,也可以使用Vite 的 import.meta.glob() 一次性查询多个文件。从这些 Markdown 文件中导出的数据随后可以在 .astro 组件中使用。

如果你有一组相关的 Markdown 文件,可以考虑将它们定义为内容集合。这会带来几个优势,包括能够将 Markdown 文件存储在文件系统中的任何位置或远程位置。

内容集合使用针对内容优化的 API 来查询和渲染你的 Markdown 内容,而不是使用文件导入。内容集合适用于共享相同结构的数据集,例如博客文章或产品项目。当你在 schema 中定义该结构时,你还可以在编辑器中获得验证、类型安全和智能提示(Intellisense)功能。

查看更多关于何时使用内容集合而不是文件导入的信息。

在导入或查询 Markdown 文件后,你可以在 .astro 组件中编写动态的 HTML 模板,其中包含 frontmatter 数据和正文内容。

src/pages/posts/great-post.md
---
title: 'The greatest post of all time'
author: 'Ben'
---
Here is my _great_ post!
src/pages/my-posts.astro
---
import * as greatPost from './posts/great-post.md';
const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));
---
<p>{greatPost.frontmatter.title}</p>
<p>Written by: {greatPost.frontmatter.author}</p>
<p>Post Archive:</p>
<ul>
{posts.map(post => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
</ul>

当使用辅助函数 getCollection()getEntry() 从你的内容集合中获取数据时,你的 Markdown 的 frontmatter 属性可在 data 对象上找到(例如 post.data.title)。此外,body 包含原始、未编译的正文内容,格式为字符串。

render() 函数会返回你的 Markdown 正文内容、一个生成的标题列表,以及一个在应用任何 remark 或 rehype 插件后修改过的 frontmatter 对象。

阅读更多关于使用内容集合查询返回的内容的信息。

当使用 importimport.meta.glob() 导入 Markdown 时,以下导出的属性在你的 .astro 组件中可用:

  • file - 文件的绝对路径(例如 /home/user/projects/.../file.md)。
  • url - 页面的 URL(例如 /zh-cn/guides/markdown-content)。
  • frontmatter - 包含文件中 YAML(或 TOML)frontmatter 中指定的任何数据。
  • <Content /> - 一个返回文件完整渲染后内容的组件。
  • rawContent() - 一个以字符串形式返回原始 Markdown 文档的函数。
  • compiledContent() - 一个异步函数,它将编译后的 Markdown 文档作为 HTML 字符串返回。
  • getHeadings() - 一个异步函数,它返回文件中所有标题(<h1><h6>)的数组,类型为:{ depth: number; slug: string; text: string }[]。每个标题的 slug 对应于为该标题生成的 ID,可用于锚点链接。

一个示例 Markdown 博客文章可能会传递以下 Astro.props 对象:

Astro.props = {
file: "/home/user/projects/.../file.md",
url: "/en/guides/markdown-content/",
frontmatter: {
/** Frontmatter from a blog post */
title: "Astro 0.18 Release",
date: "Tuesday, July 27 2021",
author: "Matthew Phillips",
description: "Astro 0.18 is our biggest release since Astro launch.",
},
getHeadings: () => [
{"depth": 1, "text": "Astro 0.18 Release", "slug": "astro-018-release"},
{"depth": 2, "text": "Responsive partial hydration", "slug": "responsive-partial-hydration"}
/* ... */
],
rawContent: () => "# Astro 0.18 Release\nA little over a month ago, the first public beta [...]",
compiledContent: () => "<h1>Astro 0.18 Release</h1>\n<p>A little over a month ago, the first public beta [...]</p>",
}

The <Content /> 组件

标题为“ 组件”的部分

<Content /> 组件可通过从 Markdown 文件中导入 Content 来获得。该组件返回文件的完整正文内容,并渲染为 HTML。你可以选择将 Content 重命名为你喜欢的任何组件名称。

你也可以通过渲染一个 <Content /> 组件来渲染 Markdown 内容集合条目的 HTML 内容

src/pages/content.astro
---
// Import statement
import {Content as PromoBanner} from '../components/promoBanner.md';
// Collections query
import { getEntry, render } from 'astro:content';
const product = await getEntry('products', 'shirt');
const { Content } = await render(product);
---
<h2>Today's promo</h2>
<PromoBanner />
<p>Sale Ends: {product.data.saleEndDate.toDateString()}</p>
<Content />

在 Markdown 中编写标题会自动为你生成锚点链接,这样你就可以直接链接到页面的特定部分。

src/pages/page-1.md
---
title: My page of content
---
## Introduction
I can link internally to [my conclusion](#conclusion) on the same page when writing Markdown.
## Conclusion
I can visit `https://example.com/page-1/#introduction` in a browser to navigate directly to my Introduction.

Astro 基于 github-slugger 生成标题 id。你可以在 `github-slugger` 文档中找到更多示例。

Astro 会在 Markdown 和 MDX 文件中的所有标题元素(<h1><h6>)中注入一个 id 属性。你可以从导入文件的Markdown 导出属性 getHeadings() 工具中获取这些数据,或者在使用内容集合查询返回的 Markdown 时从 render() 函数中获取。

你可以通过添加注入 id 属性的 rehype 插件(例如 rehype-slug)来自定义这些标题 ID。你的自定义 ID(而不是 Astro 的默认 ID)将反映在 HTML 输出和 getHeadings() 返回的项中。

默认情况下,Astro 会在你的 rehype 插件运行之后注入 id 属性。如果你的某个自定义 rehype 插件需要访问由 Astro 注入的 ID,你可以直接导入并使用 Astro 的 rehypeHeadingIds 插件。请确保在任何依赖它的插件之前添加 rehypeHeadingIds

astro.config.mjs
import { defineConfig } from 'astro/config';
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import { otherPluginThatReliesOnHeadingIDs } from 'some/plugin/source';
export default defineConfig({
markdown: {
rehypePlugins: [
rehypeHeadingIds,
otherPluginThatReliesOnHeadingIDs,
],
},
});

Astro 中的 Markdown 支持由 remark 提供,它是一个强大的解析和处理工具,拥有活跃的生态系统。目前不支持其他 Markdown 解析器,如 Pandoc 和 markdown-it。

Astro 默认应用 GitHub-flavored MarkdownSmartyPants 插件。这带来了一些便利功能,比如从文本生成可点击链接,以及格式化引号和破折号

你可以在 astro.config.mjs 中自定义 remark 解析 Markdown 的方式。查看完整的Markdown 配置选项列表

Astro 支持为 Markdown 添加第三方的 remarkrehype 插件。这些插件可以让你用新功能扩展你的 Markdown,比如自动生成目录应用无障碍的表情符号标签以及为你的 Markdown 设置样式

我们鼓励你浏览 awesome-remarkawesome-rehype 来查找流行的插件!请参阅每个插件自己的 README 以获取具体的安装说明。

此示例将 remark-tocrehype-accessible-emojis 应用于 Markdown 文件:

astro.config.mjs
import { defineConfig } from 'astro/config';
import remarkToc from 'remark-toc';
import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis';
export default defineConfig({
markdown: {
remarkPlugins: [ [remarkToc, { heading: 'toc', maxDepth: 3 } ] ],
rehypePlugins: [rehypeAccessibleEmojis],
},
});

为了自定义插件,请在插件后面的嵌套数组中提供一个选项对象。

下面的示例向 remarkToc 插件添加了 `heading` 选项,以更改目录的位置;并向 rehype-autolink-headings 插件添加了 `behavior` 选项,以便在标题文本后添加锚点标签。

astro.config.mjs
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default {
markdown: {
remarkPlugins: [ [remarkToc, { heading: "contents"} ] ],
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'append' }]],
},
}

你可以通过使用remark 或 rehype 插件,向所有 Markdown 和 MDX 文件添加 frontmatter 属性。

  1. 从插件的 file 参数中,将一个 customProperty 附加到 data.astro.frontmatter 属性上:

    example-remark-plugin.mjs
    export function exampleRemarkPlugin() {
    // All remark and rehype plugins return a separate function
    return function (tree, file) {
    file.data.astro.frontmatter.customProperty = 'Generated property';
    }
    }
  2. 将此插件应用到你的 markdownmdx 集成配置中:

    astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { exampleRemarkPlugin } from './example-remark-plugin.mjs';
    export default defineConfig({
    markdown: {
    remarkPlugins: [exampleRemarkPlugin]
    },
    });

    astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { exampleRemarkPlugin } from './example-remark-plugin.mjs';
    export default defineConfig({
    integrations: [
    mdx({
    remarkPlugins: [exampleRemarkPlugin],
    }),
    ],
    });

现在,每个 Markdown 或 MDX 文件都将在其 frontmatter 中拥有 customProperty,使其在导入 markdown 时可用,并可以通过布局中的 Astro.props.frontmatter 属性访问。

相关指南: 添加阅读时间

Astro 的 MDX 集成默认会继承你项目现有的 Markdown 配置。要覆盖单个选项,你可以在 MDX 配置中指定它们的等效项。

以下示例禁用了 GitHub-Flavored Markdown,并为 MDX 文件应用了一组不同的 remark 插件:

astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
markdown: {
syntaxHighlight: 'prism',
remarkPlugins: [remarkPlugin1],
gfm: true,
},
integrations: [
mdx({
// `syntaxHighlight` inherited from Markdown
// Markdown `remarkPlugins` ignored,
// only `remarkPlugin2` applied.
remarkPlugins: [remarkPlugin2],
// `gfm` overridden to `false`
gfm: false,
})
]
});

为避免从 MDX 继承你的 Markdown 配置,请将 extendMarkdownConfig 选项(默认启用)设置为 false

astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
markdown: {
remarkPlugins: [remarkPlugin],
},
integrations: [
mdx({
// Markdown config now ignored
extendMarkdownConfig: false,
// No `remarkPlugins` applied
})
]
});

Astro 将 /src/pages/ 目录内的任何支持的文件类型都视为一个页面,包括 .md 和其他 Markdown 文件类型。

将文件放置在此目录或任何子目录中,将自动使用文件的路径名构建页面路由,并显示渲染为 HTML 的 Markdown 内容。Astro 会自动向你的页面添加一个 <meta charset="utf-8"> 标签,以便更轻松地创作非 ASCII 内容。

src/pages/page-1.md
---
title: Hello, World
---
# Hi there!
This Markdown file creates a page at `your-domain.com/page-1/`
It probably isn't styled much, but Markdown does support:
- **bold** and _italics._
- lists
- [links](https://astro.js.cn)
- <p>HTML elements</p>
- and more!

为了解决独立 Markdown 页面功能有限的问题,Astro 提供了一个特殊的 frontmatter layout 属性,它是一个指向 Astro Markdown 布局组件的相对路径。当使用内容集合查询和渲染 Markdown 内容时,layout 不是一个特殊属性,并且不保证在其预期用例之外得到支持。

如果你的 Markdown 文件位于 src/pages/ 内,请创建一个布局组件,并在此布局属性中添加它,以便为你的 Markdown 内容提供一个页面外壳。

src/pages/posts/post-1.md
---
layout: ../../layouts/BlogPostLayout.astro
title: Astro in brief
author: Himanshu
description: Find out what makes Astro awesome!
---
This is a post written in Markdown.

这个布局组件是一个常规的 Astro 组件,它通过 Astro.props 为你的 Astro 模板自动提供特定的属性。例如,你可以通过 Astro.props.frontmatter 访问 Markdown 文件的 frontmatter 属性:

src/layouts/BlogPostLayout.astro
---
const {frontmatter} = Astro.props;
---
<html>
<head>
<!-- ... -->
<meta charset="utf-8"> // no longer added by default
</head>
<!-- ... -->
<h1>{frontmatter.title}</h1>
<h2>Post author: {frontmatter.author}</h2>
<p>{frontmatter.description}</p>
<slot /> <!-- Markdown content is injected here -->
<!-- ... -->
</html>

当使用 frontmatter 的 layout 属性时,你必须在布局中包含 <meta charset="utf-8"> 标签,因为 Astro 将不再自动添加它。现在你还可以在布局组件中为你的 Markdown 设置样式

了解更多关于Markdown 布局的信息。

除了内容集合,Astro 不内置支持远程 Markdown。

要直接获取远程 Markdown 并将其渲染为 HTML,你需要从 NPM 安装并配置你自己的 Markdown 解析器。这将不会继承你已配置的任何 Astro 内置 Markdown 设置。

在你的项目中实现此功能之前,请确保你了解这些限制,并考虑使用内容集合加载器来获取你的远程 Markdown。

src/pages/remote-example.astro
---
// Example: Fetch Markdown from a remote API
// and render it to HTML, at runtime.
// Using "marked" (https://github.com/markedjs/marked)
import { marked } from 'marked';
const response = await fetch('https://raw.githubusercontent.com/wiki/adam-p/markdown-here/Markdown-Cheatsheet.md');
const markdown = await response.text();
const content = marked.parse(markdown);
---
<article set:html={content} />
贡献 社区 赞助