作者:Titus Wormer
注入组件
本指南展示了如何在 MDX 运行时注入任意组件。 它展示了我们的 provider(@mdx-js/react、 @mdx-js/preact)和 Next.js 支持的 mdx-components.tsx 文件 使用的底层特性, 以及你如何自己利用该功能。
在许多情况下你不需要这个, 因为你可以向 MDX 传递组件:
# Hello *<Planet />*
你可以传递 Planet 和一个用于代替 h1 的组件:
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 ExampleAn function component which renders the MDX content using JSX.
- @param props This value is be available as the named variable
propsinside 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: ConsoleThe 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
Consoleclass with methods such asconsole.log(),console.error()andconsole.warn()that can be used to write to any Node.js stream. - A global
consoleinstance configured to write toprocess.stdoutandprocess.stderr. The globalconsolecan be used without importing thenode:consolemodule.
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
- @see source
(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 ExampleAn function component which renders the MDX content using JSX.
- @param props This value is be available as the named variable
propsinside 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?: MDXComponentsThis prop may be used to customize how certain components are rendered.
(method) Planet(): string(method) h1(properties: JSX.IntrinsicElements): JSX.Element(parameter) properties: JSX.IntrinsicElements(property) JSX.IntrinsicElements.h2: DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>(parameter) properties: JSX.IntrinsicElements当你发现自己在大量传递那个 components prop 时, 你可能想看看替代方案。 你可能会使用我们基于上下文的 provider(@mdx-js/react、 @mdx-js/preact), 但上下文有性能缺点,而且上下文不总是有效(例如 在 RSC 中)。
但首先, 组件传递是如何工作的? 可以通过查看 MDX 为上面的 example.mdx 生成的代码来说明。 这是一个 diff,显示了示例正常编译为什么以及传递 providerImportSource: 'xxx' 时的变化:
@@ -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 导出。
我们可以用这个接口从文件中注入组件。 在那个文件中, 我们需要一个返回我们组件的 useMDXComponents 函数。
/**
* @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(method) h1(properties: JSX.IntrinsicElements): JSX.Element(parameter) properties: JSX.IntrinsicElements(property) JSX.IntrinsicElements.h2: DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>(parameter) properties: JSX.IntrinsicElements现在将文件路径或 URL 作为 providerImportSource 传递给该文件, 例如使用 import.meta.resolve('./mdx-components.js'):
@@ -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 文件!