在 Astro 中使用 Markdown
Markdown 通常用于编写像博客文章和文档这样文本量大的内容。Astro 内置了对 Markdown 文件的支持,这些文件还可以包含 frontmatter YAML(或 TOML)来定义自定义属性,如标题、描述和标签。
在 Astro 中,你可以使用 GitHub Flavored Markdown 编写内容,然后在 .astro
组件中渲染它。这结合了一种为内容设计的熟悉的写作格式与 Astro 组件语法和架构的灵活性。
为了获得额外的功能,例如在 Markdown 中包含组件和 JSX 表达式,可以添加 @astrojs/mdx
集成,以使用 MDX 编写你的 Markdown 内容。
组织 Markdown 文件
标题为“组织 Markdown 文件”的部分你的本地 Markdown 文件可以存放在 src/
目录下的任何位置。位于 src/pages/
内的 Markdown 文件将自动在你的网站上生成Markdown 页面。
你的 Markdown 内容和 frontmatter 属性可以通过本地文件导入在组件中使用,或者通过内容集合辅助函数获取数据并查询和渲染后使用。
文件导入 vs 内容集合查询
标题为“文件导入 vs 内容集合查询”的部分本地 Markdown 可以通过单个文件的 import
语句导入到 .astro
组件中,也可以使用Vite 的 import.meta.glob()
一次性查询多个文件。从这些 Markdown 文件中导出的数据随后可以在 .astro
组件中使用。
如果你有一组相关的 Markdown 文件,可以考虑将它们定义为内容集合。这会带来几个优势,包括能够将 Markdown 文件存储在文件系统中的任何位置或远程位置。
内容集合使用针对内容优化的 API 来查询和渲染你的 Markdown 内容,而不是使用文件导入。内容集合适用于共享相同结构的数据集,例如博客文章或产品项目。当你在 schema 中定义该结构时,你还可以在编辑器中获得验证、类型安全和智能提示(Intellisense)功能。
动态的类 JSX 表达式
标题为“动态的类 JSX 表达式”的部分在导入或查询 Markdown 文件后,你可以在 .astro
组件中编写动态的 HTML 模板,其中包含 frontmatter 数据和正文内容。
---title: 'The greatest post of all time'author: 'Ben'---
Here is my _great_ post!
---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>
可用的属性
标题为“可用的属性”的部分来自内容集合查询的 Markdown
标题为“来自内容集合查询的 Markdown”的部分当使用辅助函数 getCollection()
或 getEntry()
从你的内容集合中获取数据时,你的 Markdown 的 frontmatter 属性可在 data
对象上找到(例如 post.data.title
)。此外,body
包含原始、未编译的正文内容,格式为字符串。
render()
函数会返回你的 Markdown 正文内容、一个生成的标题列表,以及一个在应用任何 remark 或 rehype 插件后修改过的 frontmatter 对象。
导入 Markdown
标题为“导入 Markdown”的部分当使用 import
或 import.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 内容。
---// Import statementimport {Content as PromoBanner} from '../components/promoBanner.md';
// Collections queryimport { 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 />
标题 ID
标题为“标题 ID”的部分在 Markdown 中编写标题会自动为你生成锚点链接,这样你就可以直接链接到页面的特定部分。
---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` 文档中找到更多示例。
标题 ID 和插件
标题为“标题 ID 和插件”的部分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
:
import { defineConfig } from 'astro/config';import { rehypeHeadingIds } from '@astrojs/markdown-remark';import { otherPluginThatReliesOnHeadingIDs } from 'some/plugin/source';
export default defineConfig({ markdown: { rehypePlugins: [ rehypeHeadingIds, otherPluginThatReliesOnHeadingIDs, ], },});
Markdown 插件
标题为“Markdown 插件”的部分Astro 中的 Markdown 支持由 remark 提供,它是一个强大的解析和处理工具,拥有活跃的生态系统。目前不支持其他 Markdown 解析器,如 Pandoc 和 markdown-it。
Astro 默认应用 GitHub-flavored Markdown 和 SmartyPants 插件。这带来了一些便利功能,比如从文本生成可点击链接,以及格式化引号和破折号。
你可以在 astro.config.mjs
中自定义 remark 解析 Markdown 的方式。查看完整的Markdown 配置选项列表。
添加 remark 和 rehype 插件
标题为“添加 remark 和 rehype 插件”的部分Astro 支持为 Markdown 添加第三方的 remark 和 rehype 插件。这些插件可以让你用新功能扩展你的 Markdown,比如自动生成目录、应用无障碍的表情符号标签以及为你的 Markdown 设置样式。
我们鼓励你浏览 awesome-remark 和 awesome-rehype 来查找流行的插件!请参阅每个插件自己的 README 以获取具体的安装说明。
此示例将 remark-toc
和 rehype-accessible-emojis
应用于 Markdown 文件:
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` 选项,以便在标题文本后添加锚点标签。
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' }]], },}
以编程方式修改 frontmatter
标题为“以编程方式修改 frontmatter”的部分你可以通过使用remark 或 rehype 插件,向所有 Markdown 和 MDX 文件添加 frontmatter 属性。
-
从插件的
file
参数中,将一个customProperty
附加到data.astro.frontmatter
属性上:example-remark-plugin.mjs export function exampleRemarkPlugin() {// All remark and rehype plugins return a separate functionreturn function (tree, file) {file.data.astro.frontmatter.customProperty = 'Generated property';}}新增于:astro@2.0.0
data.astro.frontmatter
包含给定 Markdown 或 MDX 文档中的所有属性。这允许你修改现有的 frontmatter 属性,或根据现有的 frontmatter 计算新属性。 -
将此插件应用到你的
markdown
或mdx
集成配置中: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
属性访问。

从 MDX 继承 Markdown 配置
标题为“从 MDX 继承 Markdown 配置”的部分Astro 的 MDX 集成默认会继承你项目现有的 Markdown 配置。要覆盖单个选项,你可以在 MDX 配置中指定它们的等效项。
以下示例禁用了 GitHub-Flavored Markdown,并为 MDX 文件应用了一组不同的 remark 插件:
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
:
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 }) ]});
独立的 Markdown 页面
标题为“独立的 Markdown 页面”的部分内容集合和将 Markdown 导入到 .astro
组件中为渲染 Markdown 提供了更多功能,是处理大部分内容的推荐方式。但是,有时你可能希望方便地只需将文件添加到 src/pages/
中,就能自动为你创建一个简单的页面。
Astro 将 /src/pages/
目录内的任何支持的文件类型都视为一个页面,包括 .md
和其他 Markdown 文件类型。
将文件放置在此目录或任何子目录中,将自动使用文件的路径名构建页面路由,并显示渲染为 HTML 的 Markdown 内容。Astro 会自动向你的页面添加一个 <meta charset="utf-8">
标签,以便更轻松地创作非 ASCII 内容。
---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!
Frontmatter layout
属性
标题为“Frontmatter 的 layout 属性”的部分为了解决独立 Markdown 页面功能有限的问题,Astro 提供了一个特殊的 frontmatter layout
属性,它是一个指向 Astro Markdown 布局组件的相对路径。当使用内容集合查询和渲染 Markdown 内容时,layout
不是一个特殊属性,并且不保证在其预期用例之外得到支持。
如果你的 Markdown 文件位于 src/pages/
内,请创建一个布局组件,并在此布局属性中添加它,以便为你的 Markdown 内容提供一个页面外壳。
---layout: ../../layouts/BlogPostLayout.astrotitle: Astro in briefauthor: Himanshudescription: Find out what makes Astro awesome!---This is a post written in Markdown.
这个布局组件是一个常规的 Astro 组件,它通过 Astro.props
为你的 Astro 模板自动提供特定的属性。例如,你可以通过 Astro.props.frontmatter
访问 Markdown 文件的 frontmatter 属性:
---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
标题为“获取远程 Markdown”的部分除了内容集合,Astro 不内置支持远程 Markdown。
要直接获取远程 Markdown 并将其渲染为 HTML,你需要从 NPM 安装并配置你自己的 Markdown 解析器。这将不会继承你已配置的任何 Astro 内置 Markdown 设置。
在你的项目中实现此功能之前,请确保你了解这些限制,并考虑使用内容集合加载器来获取你的远程 Markdown。
---// 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} />