Skip to navigation

MDX 按需

¥MDX on demand

本指南介绍如何使用 @mdx-js/mdx 在服务器上编译 MDX 并在客户端上运行结果。 有些框架,例如 Next.js 和 Remix,可以轻松地在服务器和客户端之间分配工作。例如,使用它可以在服务器上按需完成大部分工作,而不是在构建时完成,然后将结果数据传递给客户端,最终在客户端使用它。

¥This guide shows how to use @mdx-js/mdx to compile MDX on the server and run the result on clients. Some frameworks, such as Next.js and Remix, make it easy to split work between servers and clients. Using that it’s possible to for example do most of the work on demand on the server instead of at build time, then pass the resulting data to clients, where they finally use it.

这类似于人们有时使用 mdx-bundlernext-mdx-remote 的用途,但 MDX 也支持它。

¥This is similar to what people sometimes use mdx-bundler or next-mdx-remote for, but MDX also supports it.

快速示例

¥Quick example

在服务器上:

¥On the server:

server.js
import {compile} from '@mdx-js/mdx'

const code = String(await compile('# hi', {
  outputFormat: 'function-body',
  /* …otherOptions */
}))
// To do: send `code` to the client somehow.
(alias) function compile(vfileCompatible: any, compileOptions?: Readonly<CompileOptions> | null | undefined): Promise<VFile>
import compile

Compile MDX to JS.

  • @param vfileCompatible MDX document to parse.
  • @param compileOptions Compile configuration (optional).
  • @return Promise to compiled file.
const code: string
var String: StringConstructor
(value?: any) => string

Allows manipulation and formatting of text strings and determination and location of substrings within strings.

(alias) compile(vfileCompatible: any, compileOptions?: Readonly<CompileOptions> | null | undefined): Promise<VFile>
import compile

Compile MDX to JS.

  • @param vfileCompatible MDX document to parse.
  • @param compileOptions Compile configuration (optional).
  • @return Promise to compiled file.
(property) outputFormat?: "function-body" | "program" | null | undefined

Output format to generate (default: 'program'); in most cases 'program' should be used, it results in a whole program; internally evaluate uses 'function-body' to compile to code that can be passed to run; in some cases, you might want what evaluate does in separate steps, such as when compiling on the server and running on the client.

在客户端:

¥On the client:

client.js
import {run} from '@mdx-js/mdx'
import * as runtime from 'react/jsx-runtime'

const code = '' // To do: get `code` from server somehow.

// @ts-expect-error: `runtime` types are currently broken.
const {default: Content} = await run(code, {...runtime, baseUrl: import.meta.url})
(alias) function run(code: {
    toString(): string;
}, options: RunOptions): Promise<MDXModule>
import run

Run code compiled with outputFormat: 'function-body'.

☢️ Danger: this evals JavaScript.

  • @param code JavaScript function body to run.
  • @param options Configuration (required).
  • @return Promise to a module; the result is an object with a default field set to the component; anything else that was exported is available too.
import runtime
const code: ""
(property) MDXModule.default: MDXContent

A functional JSX component which renders the content of the MDX file.

const Content: MDXContent

A functional JSX component which renders the content of the MDX file.

(alias) run(code: {
    toString(): string;
}, options: RunOptions): Promise<MDXModule>
import run

Run code compiled with outputFormat: 'function-body'.

☢️ Danger: this evals JavaScript.

  • @param code JavaScript function body to run.
  • @param options Configuration (required).
  • @return Promise to a module; the result is an object with a default field set to the component; anything else that was exported is available too.
const code: ""
import runtime
(property) baseUrl?: any

Use this URL as import.meta.url and resolve import and export … from relative to it (optional, example: import.meta.url); this option can also be given at compile time in CompileOptions; you should pass this (likely at runtime), as you might get runtime errors when using import.meta.url / import / export … from otherwise.

The type of import.meta.

If you need to declare that a given property exists on import.meta, this type may be augmented via interface merging.

any

Content 现在是 MDXContent 组件,你可以像平常一样在框架中使用它(请参阅 § 使用 MDX)。

¥Content is now an MDXContent component that you can use like normal in your framework (see § Using MDX).

更多信息可在 @mdx-js/mdxcompilerun 的 API 文档中找到。对于其他用例,你还可以使用 evaluate,它可以同时编译和运行。

¥More information is available in the API docs of @mdx-js/mdx for compile and run. For other use cases, you can also use evaluate, which both compiles and runs in one.

注意:MDX 不是打包器(esbuild、webpack 和 Rollup 是打包器):你无法从 MDX 字符串中的服务器导入其他代码并获得一个很好缩小的包左右。

¥Note: MDX is not a bundler (esbuild, webpack, and Rollup are bundlers): you can’t import other code from the server within the string of MDX and get a nicely minified bundle out or so.

Next.js 示例

¥Next.js example

有些框架允许你将服务器和客户端代码编写在一个文件中,例如 Next。

¥Some frameworks let you write the server and client code in one file, such as Next.

pages/hello.js
/**

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

 * @import {Dispatch, SetStateAction} from 'react'
 */

import {compile, run} from '@mdx-js/mdx'
import {Fragment, useEffect, useState} from 'react'
import * as runtime from 'react/jsx-runtime'

/**

 * @param {{code: string}} props

 * @returns {JSX.Element}
 */
export default function Page({code}) {
  /** @type {[MDXModule | undefined, Dispatch<SetStateAction<MDXModule | undefined>>]} */
  const [mdxModule, setMdxModule] = useState()
  const Content = mdxModule ? mdxModule.default : Fragment

  useEffect(
    function () {
      ;(async function () {
        // @ts-expect-error: `runtime` types are currently broken.
        setMdxModule(await run(code, {...runtime, baseUrl: import.meta.url}))
      })()
    },
    [code]
  )

  return <Content />
}

export async function getStaticProps() {
  const code = String(
    await compile('# hi', {
      outputFormat: 'function-body'
      /* …otherOptions */
    })
  )
  return {props: {code}}
}
(alias) function compile(vfileCompatible: any, compileOptions?: Readonly<CompileOptions> | null | undefined): Promise<VFile>
import compile

Compile MDX to JS.

  • @param vfileCompatible MDX document to parse.
  • @param compileOptions Compile configuration (optional).
  • @return Promise to compiled file.
(alias) function run(code: {
    toString(): string;
}, options: RunOptions): Promise<MDXModule>
import run

Run code compiled with outputFormat: 'function-body'.

☢️ Danger: this evals JavaScript.

  • @param code JavaScript function body to run.
  • @param options Configuration (required).
  • @return Promise to a module; the result is an object with a default field set to the component; anything else that was exported is available too.
(alias) const Fragment: React.ExoticComponent<{
    children?: React.ReactNode | undefined;
}>
import Fragment

Lets you group elements without a wrapper node.

  • @see {@link https://react.dev/reference/react/Fragment React Docs}
  • @example
    import { Fragment } from 'react';
    
    <Fragment>
      <td>Hello</td>
      <td>World</td>
    </Fragment>
    
  • @example
    // Using the <></> shorthand syntax:
    
    <>
      <td>Hello</td>
      <td>World</td>
    </>
    
(alias) function useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void
import useEffect

Accepts a function that contains imperative, possibly effectful code.

  • @param effect Imperative function that can return a cleanup function
  • @param deps If present, effect will only activate if the values in the list change.
  • @version 16.8.0
  • @see {@link https://react.dev/reference/react/useEffect}
(alias) function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>] (+1 overload)
import useState

Returns a stateful value, and a function to update it.

import runtime
function Page({ code }: {
    code: string;
}): JSX.Element
  • @param props
  • @returns
(parameter) code: string
const mdxModule: MDXModule | undefined
const setMdxModule: Dispatch<SetStateAction<MDXModule | undefined>>
(alias) useState<MDXModule>(): [MDXModule | undefined, Dispatch<SetStateAction<MDXModule | undefined>>] (+1 overload)
import useState

Returns a stateful value, and a function to update it.

const Content: React.ExoticComponent<{
    children?: React.ReactNode | undefined;
}> | MDXContent
const mdxModule: MDXModule | undefined
const mdxModule: MDXModule
(property) MDXModule.default: MDXContent

A functional JSX component which renders the content of the MDX file.

(alias) const Fragment: React.ExoticComponent<{
    children?: React.ReactNode | undefined;
}>
import Fragment

Lets you group elements without a wrapper node.

  • @see {@link https://react.dev/reference/react/Fragment React Docs}
  • @example
    import { Fragment } from 'react';
    
    <Fragment>
      <td>Hello</td>
      <td>World</td>
    </Fragment>
    
  • @example
    // Using the <></> shorthand syntax:
    
    <>
      <td>Hello</td>
      <td>World</td>
    </>
    
(alias) useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void
import useEffect

Accepts a function that contains imperative, possibly effectful code.

  • @param effect Imperative function that can return a cleanup function
  • @param deps If present, effect will only activate if the values in the list change.
  • @version 16.8.0
  • @see {@link https://react.dev/reference/react/useEffect}
const setMdxModule: (value: SetStateAction<MDXModule | undefined>) => void
(alias) run(code: {
    toString(): string;
}, options: RunOptions): Promise<MDXModule>
import run

Run code compiled with outputFormat: 'function-body'.

☢️ Danger: this evals JavaScript.

  • @param code JavaScript function body to run.
  • @param options Configuration (required).
  • @return Promise to a module; the result is an object with a default field set to the component; anything else that was exported is available too.
(parameter) code: string
import runtime
(property) baseUrl?: any

Use this URL as import.meta.url and resolve import and export … from relative to it (optional, example: import.meta.url); this option can also be given at compile time in CompileOptions; you should pass this (likely at runtime), as you might get runtime errors when using import.meta.url / import / export … from otherwise.

The type of import.meta.

If you need to declare that a given property exists on import.meta, this type may be augmented via interface merging.

any
(parameter) code: string
const Content: React.ExoticComponent<{
    children?: React.ReactNode | undefined;
}> | MDXContent
function getStaticProps(): Promise<{
    props: {
        code: string;
    };
}>
const code: string
var String: StringConstructor
(value?: any) => string

Allows manipulation and formatting of text strings and determination and location of substrings within strings.

(alias) compile(vfileCompatible: any, compileOptions?: Readonly<CompileOptions> | null | undefined): Promise<VFile>
import compile

Compile MDX to JS.

  • @param vfileCompatible MDX document to parse.
  • @param compileOptions Compile configuration (optional).
  • @return Promise to compiled file.
(property) outputFormat?: "function-body" | "program" | null | undefined

Output format to generate (default: 'program'); in most cases 'program' should be used, it results in a whole program; internally evaluate uses 'function-body' to compile to code that can be passed to run; in some cases, you might want what evaluate does in separate steps, such as when compiling on the server and running on the client.

(property) props: {
    code: string;
}
(property) code: string
MDX 中文网 - 粤ICP备13048890号