这篇文章解释了如何将 MDX 集成到您的项目中。它展示了如何使用您选择的捆绑器和JSX 运行时与 MDX 一起使用。要了解 MDX 格式的工作原理,我们建议您从§ 什么是MDX 开始。在您设置好并准备好使用 MDX 时,请参阅使用MDX。
先决条件
MDX 依赖于 JSX,因此您的项目也必须支持 JSX。任何 JSX 运行时(React、Preact、Vue 等)都可以。请注意,我们会为您将 JSX 编译为 JavaScript,因此您不必自行设置。
所有 @mdx-js/*
包都是用现代 JavaScript 编写的。需要 Node.js 版本 16.0 或更高版本才能使用它们。我们的包也仅支持 ESM only,因此它们必须以 import
形式引入,而不是 require
形式。
注意:想使用 Rust 而不是 Node.js 吗?试试 mdxjs-rs
!
快速入门
打包器
MDX 是一种编译为 JavaScript 的语言。(我们还将常规 markdown 编译为 JavaScript。)如果您有一个打包器,最简单的开始方法就是使用适用于您的打包器的集成:
- 如果您使用 esbuild,请安装并配置
@mdx-js/esbuild
- 如果您使用 Rollup(或 Vite),请安装并配置
@mdx-js/rollup
- 如果您使用 webpack(或 Next.js),请安装并配置
@mdx-js/loader
您也可以在没有打包器的情况下使用 MDX:
- 您可以使用
@mdx-js/node-loader
在 Node.js 中导入 MDX 文件 - 您可以使用我们的核心编译器
@mdx-js/mdx
编译 MDX 文件 - 您可以使用我们的核心编译器
@mdx-js/mdx
进行评估(编译 并运行)MDX 文件
有关这些工具的更多信息,请参阅它们各自的专用部分:¶ Next.js、¶ Node.js、¶ Rollup、¶ Vite、¶ esbuild 和 ¶ webpack。
JSX
现在您已经设置了集成或 @mdx-js/mdx
本身,是时候配置您的 JSX 运行时了。
- 如果您使用 React,那就是默认的;可以选择安装并配置
@mdx-js/react
- 如果您使用 Preact,请将
ProcessorOptions
中的jsxImportSource
设置为'preact'
;可以选择安装并配置@mdx-js/preact
- 如果您使用 Svelte,请安装
svelte-jsx
,并将ProcessorOptions
中的jsxImportSource
设置为'svelte-jsx'
- 如果您使用 Vue,请将
ProcessorOptions
中的jsxImportSource
设置为'vue'
;可以选择安装并配置@mdx-js/vue
- 如果您使用 Solid,请将
ProcessorOptions
中的jsxImportSource
设置为'solid-js/h'
- 如果您使用 Emotion,请将
ProcessorOptions
中的jsxImportSource
设置为'@emotion/react'
- 如果您使用 Theme UI,请先安装并配置
@mdx-js/react
,然后将您的 MDX 内容包裹在<ThemeProvider />
中
其他 JSX 运行时可通过设置 ProcessorOptions
中的 jsxImportSource
来支持。
有关这些工具的更多信息,请参阅它们各自的专用部分:¶ Emotion、¶ Preact、¶ React、¶ Solid、¶ Svelte、¶ Theme UI 和 ¶ Vue。
编辑器
您可以通过为编辑器添加 MDX 支持来增强使用 MDX 的体验:
- 如果您使用 VS Code,请尝试
mdx-js/vscode-mdx
- 如果您使用 Vim,请尝试
jxnblk/vim-mdx-js
- 如果您使用 Sublime Text,请尝试
jonsuh/mdx-sublime
- 如果您使用 **JetBrains IntelliJ/WebStorm
**,请尝试 JetBrains/mdx-intellij-plugin
驱动我们 VS Code 扩展的语法高亮显示也用于 GitHub 上的代码块高亮显示,这些语法高亮显示的维护工作在 wooorm/markdown-tm-language
中进行。
类型
扩展类型导入的示例
首先安装该包:
Shell
npm install @types/mdx
……TypeScript 应该会自动识别到:
example.js
import Post from './post.mdx' // `Post` 现在已经有了类型。
我们的包是用 TypeScript 编写的。要使类型起作用,必须对 JSX
命名空间进行类型化。可以通过安装和使用您的框架的类型,比如 @types/react
来完成这一点。
要为导入的 .mdx
、.md
等文件启用类型,请安装并使用 @types/mdx
。该包还导出了一些有用的类型,例如 MDXComponents
,它表示 components
属性。您可以这样导入它们:
example.ts
import type {MDXComponents} from 'mdx/types.js'
安全性
MDX 是一种编程语言。如果您信任您的作者,那没问题。如果不信任,那就不安全。
不要让互联网上的随机人员编写 MDX。如果您这样做了,您可能会考虑使用带有 sandbox
的 <iframe>
,但是安全性很难保证,似乎也不是 100%。对于 Node.js,vm2 听起来很有趣。但是您可能还应该使用 Docker 或类似的东西沙盒整个操作系统,执行速率限制,并确保进程在运行时间过长时可以被终止。
集成
打包器
esbuild
扩展示例
example.js
import mdx from '@mdx-js/esbuild'
import esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['index.mdx'],
format: 'esm',
outfile: 'output.js',
plugins: [mdx({/* jsxImportSource: …, otherOptions… */})]
})
我们支持 esbuild。安装并配置 esbuild 插件 @mdx-js/esbuild
。根据您使用的 JSX 运行时(React、Preact、Vue 等),配置您的 JSX 运行时。
要使用比您的用户支持的更现代的 JavaScript 特性,配置 esbuild 的 target
。
Rollup
扩展示例
rollup.config.js
import mdx from '@mdx-js/rollup'
import {babel} from '@rollup/plugin-babel'
/** @type {import('rollup').RollupOptions} */
const config = {
// …
plugins: [
// …
mdx({/* jsxImportSource: …, otherOptions… */})
// Babel 是可选的:
babel({
// 也在之前所用的 `.mdx` 上运行(但现在是 JS):
extensions: ['.js', '.jsx', '.cjs', '.mjs', '.md', '.mdx'],
// 其他选项…
})
]
}
export default config
我们支持 Rollup。安装并配置 Rollup 插件 @mdx-js/rollup
。根据您使用的 JSX 运行时(React、Preact、Vue 等),配置您的 JSX 运行时。
要使用比您的用户支持的更现代的 JavaScript 特性,安装并配置 @rollup/plugin-babel
。
如果您通过 Vite 使用 Rollup,请参阅 ¶ Vite 获取更多信息。
webpack
扩展示例
webpack.config.js
/** @type {import('webpack').Configuration} */
const webpackConfig = {
module: {
// …
rules: [
// …
{
test: /\.mdx?$/,
use: [
// Babel 是可选的:
{loader: 'babel-loader', options: {}},
{
loader: '@mdx-js/loader',
/** @type {import('@mdx-js/loader').Options} */
options: {/* jsxImportSource: …, otherOptions… */}
}
]
}
]
}
}
export default webpackConfig
我们支持 webpack。安装并配置 webpack loader @mdx-js/loader
。根据您使用的 JSX 运行时(React、Preact、Vue 等),配置您的 JSX 运行时。
要使用比您的用户支持的更现代的 JavaScript 特性,安装并配置 babel-loader
。
如果您通过 Next 使用 webpack,请参阅 ¶ Next.js 获取更多信息。
构建系统
Vite
扩展示例
vite.config.js
import mdx from '@mdx-js/rollup'
import {defineConfig} from 'vite'
const viteConfig = defineConfig({
plugins: [
mdx(/* jsxImportSource: …, otherOptions… */)
]
})
export default viteConfig
我们支持 Vite。安装并配置 Rollup 插件 @mdx-js/rollup
。根据您使用的 JSX 运行时(React、Preact、Vue 等),配置您的 JSX 运行时。
要使用
比您的用户支持的更现代的 JavaScript 特性,配置 Vite 的 build.target
。
注意:如果您还使用了 vitejs/vite-plugin-react
,您必须在它之前强制运行 @mdx-js/rollup
的 pre
阶段:
vite.config.js
// …
const viteConfig = defineConfig({
plugins: [
{enforce: 'pre', ...mdx(/* jsxImportSource: …, otherOptions… */)},
react({include: /\.(jsx|js|mdx|md|tsx|ts)$/})
]
})
// …
另请参阅 ¶ Rollup,它在 Vite 中使用,有关更多信息,请参见 ¶ Vue。使用的更多信息。
编译器
Babel
扩展插件和示例用法
这个插件:
plugin.js
import path from 'node:path'
import parser from '@babel/parser'
import {compileSync} from '@mdx-js/mdx'
import estreeToBabel from 'estree-to-babel'
export function babelPluginSyntaxMdx() {
// 告诉 Babel 使用不同的解析器。
return {parserOverride: babelParserWithMdx}
}
// 一个 Babel 解析器,它使用 `@mdx-js/mdx` 解析 MDX 文件,并将任何其他内容传递给正常的 Babel 解析器。
function babelParserWithMdx(value, options) {
if (options.sourceFileName && /\.mdx?$/.test(options.sourceFileName)) {
// 不幸的是,Babel 不支持异步解析器。
return compileSync(
{value, path: options.sourceFileName},
// 告诉 `@mdx-js/mdx` 返回 Babel 树而不是序列化的 JS。
{recmaPlugins: [recmaBabel], /* jsxImportSource: …, otherOptions… */}
).result
}
return parser.parse(value, options)
}
// “recma” 插件是在 estree 上运行的统一插件(由 `@mdx-js/mdx` 和许多 JS 生态系统的其他部分使用,但不是 Babel)。
// 该插件将 `'estree-to-babel'` 定义为编译器,这意味着由 `compileSync` 返回 Babel 树。
function recmaBabel() {
this.compiler = estreeToBabel
}
……可以像这样使用 Babel API:
example.js
import babel from '@babel/core'
import {babelPluginSyntaxMdx} from './plugin.js'
// 请注意,必须为我们的插件设置文件名,以便它知道这是 MDX 而不是 JS。
await babel.transformAsync(file, {filename: 'example.mdx', plugins: [babelPluginSyntaxMdx]})
最好使用 Rollup 或 webpack 而不是直接使用 Babel,因为这提供了最好的接口。可以在 Babel 中使用 @mdx-js/mdx
,而且速度比较快,因为它跳过了 @mdx-js/mdx
的序列化和 Babel 解析,如果无论如何都使用了 Babel。
Babel 不支持对其解析器的语法扩展(它有“语法”插件,但那些只是打开或关闭内部标志)。它支持设置不同的解析器。这反过来又让我们可以选择使用 @mdx-js/mdx
或 @babel/parser
。
站点生成器
Astro
Astro 具有自己的 MDX 集成。您可以使用 Astro CLI 添加集成:npx astro add mdx
。
这个基本设置允许您将 markdown、Astro 组件和 MDX 文件作为组件导入。有关如何在 MDX 文件中使用框架组件的信息,请参阅 Astro 的 Framework components guide。
有关如何结合 Astro 和 MDX,请参阅 Astro 的 MDX 集成文档。
Docusaurus
Docusaurus 默认支持 MDX。有关如何在 Docusaurus 中使用 MDX 的信息,请参阅 Docusaurus 的 MDX 和 React 指南。
Gatsby
Gatsby 有自己的插件来支持 MDX。请参阅 gatsby-plugin-mdx
,了解如何在 Gatsby 中使用 MDX。
Next.js
扩展示例
next.config.js
import nextMdx from '@next/mdx'
const withMdx = nextMdx({
// 默认情况下只支持 `.mdx` 扩展名。
extension: /\.mdx?$/,
options: {/* otherOptions… */}
})
const nextConfig = withMdx({
// 将 MDX 文件作为页面支持:
pageExtensions: ['md', 'mdx', 'tsx', 'ts', 'jsx', 'js'],
})
export default nextConfig
Next.js 具有自己的 MDX 集成。安装并配置 @next/mdx
。
请勿在 Next 中使用 providerImportSource
和 @mdx-js/react
注入组件。而是在 src/
或 /
中添加一个 mdx-components.tsx
文件。有关更多信息,请参阅 在 nextjs.org
上配置 MDX。
Parcel
Parcel 有自己的插件来支持 MDX。请参阅 @parcel/transformer-mdx
,了解如何在 Parcel 中使用 MDX。
JSX 运行时
Emotion
扩展示例
example.js
import {CacheProvider} from '@emotion/react'
import {mdx} from '@mdx-js/react'
import {cache} from 'emotion'
/** @type {import('@mdx-js/react').MDXProviderProps['components']} */
const components = {}
const content = mdx('Hello, world!', {components})
// 在组件中:
ReactDOM.render(
<CacheProvider value={cache}>{content}</CacheProvider>,
document.body
)
Emotion 是使用我们的 MDX 插件时的默认支持的 JSX 运行时。安装并配置 @mdx-js/react
。有关如何在 Emotion 中使用 MDX 的信息,请参阅 Emotion 的 MDX 文档。
Preact
扩展示例
example.js
import {h, render} from 'preact'
import {mdx} from '@mdx-js/preact'
/** @type {import('@mdx-js/preact').MDXProviderComponentsProp} */
const components = {}
const content = mdx('Hello, world!', {components})
// 在组件中:
render(content, document.body)
Preact 是使用我们的 MDX 插件时的默认支持的 JSX 运行时。安装并配置 @mdx-js/preact
。有关如何在 Preact 中使用 MDX 的信息,请参阅 Preact 的 MDX 文档。
React
扩展示例
example.js
import ReactDOM from 'react-dom'
import {mdx} from '@mdx-js/react'
/** @type {import('@mdx-js/react').MDXProviderComponentsProp} */
const components = {}
const content = mdx('Hello, world!', {components})
// 在组件中:
ReactDOM.render(content, document.body)
React 是使用我们的 MDX 插件时的默认支持的 JSX 运行时。安装并配置 @mdx-js/react
。有关如何在 React 中使用 MDX 的信息,请参阅 React 的 MDX 文档。
Solid
扩展示例
example.js
import {createSignal, JSX} from 'solid-js'
import {mdx} from '@mdx-js/solid'
const components = {} as Record<string, (props: any) => JSX.Element>
const content = mdx('Hello, world!', {components})
// 在组件中:
const [content, setContent] = createSignal<JSX.Element>()
setContent(content)
Solid 是使用我们的 MDX 插件时的默认支持的 JSX 运行时。安装并配置 @mdx-js/solid
。有关如何在 Solid 中使用 MDX 的信息,请参阅 Solid 的 MDX 文档。
Svelte
扩展示例
example.js
import {render} from '@testing-library/svelte'
import {mdx} from '@mdx-js/svelte'
/** @type {import('@mdx-js/svelte').ComponentMap} */
const components = {}
const content = mdx('Hello, world!', {components})
// 在组件中:
const {container} = render(content)
Svelte 是使用我们的 MDX 插件时的默认支持的 JSX 运行时。安装并配置 @mdx-js/svelte
。有关如何在 Svelte 中使用 MDX 的信息,请参阅 Svelte 的 MDX 文档。
Theme UI
扩展示例
example.js
import {ThemeProvider} from '@theme-ui/react'
import {mdx} from '@mdx-js/react'
import {theme} from './theme'
/** @type {import('@mdx-js/react').MDXProviderComponentsProp} */
const components = {}
const content = mdx('Hello, world!', {components})
// 在组件中:
ReactDOM.render(
<ThemeProvider theme={theme}>{content}</ThemeProvider>,
document.body
)
Theme UI 是使用我们的 MDX 插件时的默认支持的 JSX 运行时。安装并配置 @mdx-js/react
。有关如何在 Theme UI 中使用 MDX 的信息,请参阅 Theme UI 的 MDX 文档。
Vue
扩展示例
example.js
import {createApp} from 'vue'
import {mdx} from '@mdx-js/vue'
const app = createApp({/* … */})
/** @type {import('@mdx-js/vue').MDXProviderComponentsProp} */
const components = {}
const content = mdx('Hello, world!', {components})
// 在组件中:
app.mount(content)
Vue 是使用我们的 MDX 插件时的默认支持的 JSX 运行时。安装并配置 @mdx-js/vue
。有关如何在 Vue 中使用 MDX 的信息,请参阅 Vue 的 MDX 文档。
TypeScript
扩展类型导入的示例
首先安装该包:
Shell
npm install @types/mdx
……TypeScript 应该会自动识别到:
example.js
import Post from './post.mdx' // `Post` 现在已经有了类型。
我们的包是用 TypeScript 编写的。要使类型起作用,必须对 JSX
命名空间进行类型化。可以通过安装和使用您的框架的类型,比如 @types/react
来完成这一点。
要为导入的 .mdx
、.md
等文件启用类型,请安装并使用 @types/mdx
。该包还导出了一些有用的类型,例如 MDXComponents
,它表示 components
属性。您可以这样导入它们:
example.ts
import type {MDXComponents} from 'mdx/types.js'
安全性
MDX 是一种编程语言。如果您信任您的作者,那没问题。如果不信任,那就不安全。
不要让互联网上的随机人员编写 MDX。如果您这样做了,您可能会考虑使用带有 sandbox
的 <iframe>
,但是安全性很难保证,似乎也不是 100%。对于 Node.js,vm2 听起来很有趣。但是您可能还应该使用 Docker 或类似的东西沙盒整个操作系统,执行速率限制,并确保进程在运行时间过长时可以被终止。