Skip to navigation

注入组件

¥Injecting components

本指南展示了如何在 MDX 运行时将任意组件注入到 MDX 中。 它展示了我们的提供商(@mdx-js/react@mdx-js/preact)使用的底层功能和 Next.js 支持的 mdx-components.tsx 文件如何工作,以及你如何自己利用该功能。

¥This guide shows how to inject arbitrary components into MDX when it runs. It shows how the underlying features used by our providers (@mdx-js/react, @mdx-js/preact) and the mdx-components.tsx file supported by Next.js work, and how you can take advantage of that functionality yourself.

在许多情况下,你不需要这样做,因为你可以将组件传递给 MDX:

¥In many cases you do not need this, as you can pass components to MDX:

example.mdx
# Hello *<Planet />*

你可以传递 Planet 并说出使用的组件来代替 h1

¥You can pass Planet and say a component used instead of the h1:

example.jsx
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.

<Example
  components={{
    Planet() {
      return 'Pluto'
    },
    h1(properties) {
      return <h2 {...properties} />
    }
  }}
/>

当你发现自己经常传递 components 属性时,你可能需要考虑替代方案。你可能会寻求我们基于上下文的提供程序(@mdx-js/react@mdx-js/preact),但上下文具有性能缺点,并且上下文并不总是有效(例如在 RSC 中)。

¥When you find yourself passing that components prop around a lot, you might want to look at an alternative. You might reach for our context based providers (@mdx-js/react, @mdx-js/preact), but context has performance downsides and context doesn’t always work (such as in RSC).

但首先,组件传递是如何工作的?可以通过查看 MDX 为上述 example.mdx 生成的代码来说明这一点。这是一个差异,显示了示例通常编译的内容以及传递 providerImportSource: 'xxx' 时发生的变化:

¥But first, how does component passing work? That can be illustrated by looking at the code generated by MDX for the above example.mdx. Here is a diff that shows what the example normally compiles to and what changes when providerImportSource: 'xxx' is passed:

Diff
@@ -1,7 +1,13 @@
 import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
+import {useMDXComponents as _provideComponents} from 'xxx'

 function _createMdxContent(props) {
-  const _components = {em: 'em', h1: 'h1', ...props.components}
+  const _components = {
+    em: 'em',
+    h1: 'h1',
+    ..._provideComponents(),
+    ...props.components
+  }
   const {Planet} = _components
   if (!Planet) _missingMdxReference('Planet', true)
   return _jsxs(_components.h1, {
@@ -10,7 +16,7 @@ function _createMdxContent(props) {
 }

 export default function MDXContent(props = {}) {
-  const {wrapper: MDXLayout} = props.components || {}
+  const {wrapper: MDXLayout} = {..._provideComponents(), ...props.components}
   return MDXLayout
     ? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {...props})})
     : _createMdxContent(props)

请注意,组件具有默认值(例如 h1 将使用 'h1'),并且组件取自 props.components。所改变的是添加了对 _provideComponents 的调用,它指的是我们指定的模块 (xxx) 的 useMDXComponents 导出。

¥Observe that components have defaults (such as that h1 will use 'h1') and that components are taken from props.components. What changes is an added call to _provideComponents, which refers to an useMDXComponents export from the module we specified (xxx).

我们可以使用这个接口从文件中注入组件。在该文件中,我们需要一个返回组件的 useMDXComponents 函数。

¥We can use this interface to inject components from a file. In that file, we need a useMDXComponents function that returns our components.

mdx-components.js
/** @returns {import('mdx/types.js').MDXComponents} */
export function useMDXComponents() {
  return {
    Planet() {
      return 'Pluto'
    },
    h1(properties) {
      return <h2 {...properties} />
    }
  }
}

现在将文件路径或 URL 作为 providerImportSource 传递到该文件,例如使用 import.meta.resolve('./mdx-components.js')

¥And now passing a file path or URL to that file as providerImportSource, such as with import.meta.resolve('./mdx-components.js'):

Diff
@@ -1,5 +1,5 @@
 import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
-import {useMDXComponents as _provideComponents} from 'xxx'
+import {useMDXComponents as _provideComponents} from 'file:///Users/tilde/…/mdx-components.js'

现在我们本地定义的组件将在所有 MDX 文件中使用!

¥Now our locally defined components will be used in all MDX files!

MDX 中文网 - 粤ICP备13048890号