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.

console.log(
  <Example
    components={{
      Planet() {
        return 'Pluto'
      },
      h1(properties) {
        return <h2 {...properties} />
      }
    }}
  />
)
(alias) function Example(props: MDXProps): Element
import Example

An function component which renders the MDX content using JSX.

  • @param props This value is be available as the named variable props inside the MDX component.
  • @returns A JSX element. The meaning of this may depend on the project configuration. I.e. it could be a React, Preact, or Vuex element.
namespace console
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without calling require('console').

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
//   Error: Whoops, something bad happened
//     at [eval]:5:15
//     at Script.runInThisContext (node:vm:132:18)
//     at Object.runInThisContext (node:vm:309:38)
//     at node:internal/process/execution:77:19
//     at [eval]-wrapper:6:22
//     at evalScript (node:internal/process/execution:76:60)
//     at node:internal/main/eval_string:23:3

const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);

myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
(method) Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

  • @since v0.1.100
(alias) function Example(props: MDXProps): Element
import Example

An function component which renders the MDX content using JSX.

  • @param props This value is be available as the named variable props inside the MDX component.
  • @returns A JSX element. The meaning of this may depend on the project configuration. I.e. it could be a React, Preact, or Vuex element.
(property) MDXProps.components?: MDXComponents

This prop may be used to customize how certain components are rendered.

(method) Planet(): string
(property) h1?: Component<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(property) JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>

当你发现自己经常传递 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
/**

 * @import {MDXComponents} from 'mdx/types.js'
 */

/** @returns {MDXComponents} */
export function useMDXComponents() {
  return {
    Planet() {
      return 'Pluto'
    },
    h1(properties) {
      return <h2 {...properties} />
    }
  }
}
function useMDXComponents(): MDXComponents
  • @returns
(method) Planet(): string
(property) h1?: Component<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(property) JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>

现在将文件路径或 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号