作者:
    Creation:2025-02-07Last update:2026-09-21

    Markdown / 富文本内容

    Intlayer 支持使用 Markdown 语法定义的富文本内容。这使您可以轻松编写和维护具有丰富格式的内容,例如博客、文章等。

    声明 Markdown 内容

    您可以使用 md 函数声明 Markdown 内容,或简单地将其作为字符串声明(如果它包含 Markdown 语法)。

    8.10.0 版本开始,您可以在 .content.md 文件中直接声明 Markdown 内容。Intlayer 会自动检测并解析 Markdown 内容。

    markdown-file.en.content.md
    ---
    key: my-markdown-content
    description: 我的内容
    locale: en
    ---
    
    # 我的内容
    
    这里是一个 Markdown 内容示例
    

    locale front-matter 字段是定义内容语言环境的字段。它是可选的。如果没有提供,Intlayer 会使用默认语言环境,如果没有提供特定语言环境的翻译,这也将作为后备语言环境。

    文件结构示例:

    text
    content
    ├── markdown-file.en.content.md
    ├── markdown-file.fr.content.md
    └── markdown-file.es.content.md
    

    您可以在 front-matter 中添加任何在字典定义中定义的属性。

    使用 md 函数显式声明 Markdown 内容。如果您想确保即使字符串不包含明显的语法也被视为 Markdown,这将非常有用。

    markdownDictionary.content.ts
    import { md, type Dictionary } from "intlayer";
    
    const markdownDictionary = {
    key: "app",
    content: {
      myMarkdownContent: md("## 我的标题 \n\nLorem Ipsum"),
    },
    } satisfies Dictionary;
    
    export default markdownDictionary;
    

    使用 file 函数直接导入 .md 文件。

    markdownDictionary.content.ts
    import { md, file, t } from "intlayer";
    
    export default {
    key: "app",
    content: {
      content: t({
        en: md(file("./myMarkdown.en.md")),
        zh: md(file("./myMarkdown.zh.md")),
      }),
    },
    };
    

    如果字符串包含常见的 Markdown 指示符(如标题、列表、链接等),Intlayer 会自动转换它。

    markdownDictionary.content.ts
    export default {
    key: "app",
    contentAutoTransformation: true, // 启用 Markdown 内容的自动检测 - 可以在 intlayer.config.ts 中全局设置
    content: {
      myMarkdownContent: "## 我的标题 \n\nLorem Ipsum",
    },
    };
    

    渲染 Markdown

    Intlayer 提供两种独立的方式来渲染 Markdown:

    1. 通过 useIntlayer — Intlayer 会自动将 md 节点转换为框架的原生输出(JSX,VNode,HTML 字符串)。

      • Frontmatter 被解析并作为 .metadata 暴露。您可以在两个级别上覆盖渲染 — 使用 MarkdownProvider(或框架等效物)进行全局覆盖,或使用 .use() 针对每个节点进行局部覆盖。两者可以结合使用;.use() 优先于 MarkdownProvider,而 MarkdownProvider 优先于默认渲染。
    2. 辅助实用程序<MarkdownRenderer />useMarkdownRenderer()renderMarkdown() 是独立的工具,仅接受原始 Markdown 字符串。它们独立于 useIntlayer,不与它返回的修饰节点一起工作。

    Markdown 渲染支持 MDX — 在您的 Markdown 中直接按名称使用任何 JSX/框架组件。

    1. 自动渲染(通过 useIntlayer

    Markdown 节点可以直接呈现为 JSX。

    App.tsx
    import { useIntlayer } from "react-intlayer";
    import { MarkdownProvider } from "react-intlayer/markdown";
    
    const AppContent = () => {
    const { myMarkdownContent } = useIntlayer("app");
    
    return <div>{myMarkdownContent}</div>;
    };
    
    const App = () => (
    <MarkdownProvider
      components={{
        h1: ({ children }) => <h1 style={{ color: "red" }}>{children}</h1>,
        MyButton: (props) => <button {...props} />, // MDX 组件
      }}
    >
      <AppContent />
    </MarkdownProvider>
    );
    
    如果没有提供 MarkdownProvider,Intlayer 将使用默认的 Markdown 转 JSX 解析器渲染 markdown。

    您还可以使用 .use() 方法提供特定节点的局部覆盖:

    tsx
    {myMarkdownContent.use({
    h1: ({ children }) => <h1 style={{ color: "red" }}>{children}</h1>,
    })}
    

    您可以以字符串形式检索 Markdown:

    tsx
    {myMarkdownContent.value}
    {String(myMarkdownContent)}
    {myMarkdownContent.toString()}
    

    您可以像这样访问您的 markdown 元数据:

    tsx
    {myMarkdownContent.metadata}
    {myMarkdownContent.metadata.title}
    

    Markdown 节点可以直接呈现为 JSX。

    App.tsx
    import { useIntlayer } from "next-intlayer";
    import { MarkdownProvider } from "next-intlayer/markdown";
    
    const AppContent = () => {
    const { myMarkdownContent } = useIntlayer("app");
    
    return <div>{myMarkdownContent}</div>;
    };
    
    const App = () => (
    <MarkdownProvider
      components={{
        h1: ({ children }) => <h1 style={{ color: "red" }}>{children}</h1>,
        MyButton: (props) => <button {...props} />, // MDX 组件
      }}
    >
      <AppContent />
    </MarkdownProvider>
    );
    
    如果没有提供 MarkdownProvider,Intlayer 将使用默认的 Markdown 转 JSX 解析器渲染 markdown。

    您还可以使用 .use() 方法提供特定节点的局部覆盖:

    tsx
    {myMarkdownContent.use({
    h1: ({ children }) => <h1 style={{ color: "red" }}>{children}</h1>,
    })}
    

    您可以以字符串形式检索 Markdown:

    tsx
    {myMarkdownContent.value}
    {String(myMarkdownContent)}
    {myMarkdownContent.toString()}
    

    您可以像这样访问您的 markdown 元数据:

    tsx
    {myMarkdownContent.metadata}
    {myMarkdownContent.metadata.title}
    

    在 Vue 中,Markdown 内容可以使用内置的 component 或直接作为节点来呈现。

    App.vue
    <script setup>
    import { useIntlayer } from "vue-intlayer";
    const { myMarkdownContent } = useIntlayer("app");
    </script>
    
    <template>
    <component :is="myMarkdownContent" />
    </template>
    

    通过 intlayerMarkdown 插件进行全局配置(支持 MDX 自定义组件):

    main.ts
    import { intlayerMarkdown } from "vue-intlayer/markdown";
    
    app.use(intlayerMarkdown, {
    components: {
      h1: (props) => h('h1', { style: { color: 'green' } }, props.children),
      MyButton: (props) => h('button', props), // MDX 组件
    },
    });
    
    如果未安装 intlayerMarkdown 插件,Intlayer 将使用默认编译器进行渲染。

    您还可以使用 .use() 方法提供特定节点的局部覆盖:

    vue
    <component :is="myMarkdownContent.use({
    h1: (props) => h('h1', { style: { color: 'red' } }, props.children),
    })" />
    

    您可以以字符串形式检索 Markdown:

    vue
    {{ myMarkdownContent.value }}
    {{ String(myMarkdownContent) }}
    {{ myMarkdownContent.toString() }}
    

    您可以像这样访问您的 markdown 元数据:

    vue
    <component :is="myMarkdownContent.metadata" />
    <component :is="myMarkdownContent.metadata.title" />
    

    默认情况下,Svelte 将 Markdown 呈现为 HTML 字符串。使用 {@html} 来渲染它。

    App.svelte
    <script lang="ts">
    import { useIntlayer } from "svelte-intlayer";
    import { MarkdownProvider } from "svelte-intlayer/markdown";
    import MyHeading from "./MyHeading.svelte";
    
    const content = useIntlayer("app");
    </script>
    
    <MarkdownProvider components={{ h1: MyHeading }}>
    {@html $content.myMarkdownContent}
    </MarkdownProvider>
    
    如果未提供 MarkdownProvider,Intlayer 将使用默认编译器渲染 markdown。

    您还可以使用 .use() 方法提供特定节点的局部覆盖:

    svelte
    {@html $content.myMarkdownContent.use({ ... })}
    

    您可以以字符串形式检索 Markdown:

    svelte
    {$content.myMarkdownContent.value}
    {String($content.myMarkdownContent)}
    {$content.myMarkdownContent.toString()}
    

    您可以像这样访问您的 markdown 元数据:

    svelte
    {$content.myMarkdownContent.metadata}
    {$content.myMarkdownContent.metadata.title}
    

    Preact 直接支持 JSX 中的 Markdown 节点。

    App.tsx
    import { useIntlayer } from "preact-intlayer";
    import { MarkdownProvider } from "preact-intlayer/markdown";
    
    const AppContent = () => {
    const { myMarkdownContent } = useIntlayer("app");
    return <div>{myMarkdownContent}</div>;
    };
    
    const App = () => (
    <MarkdownProvider
      components={{
        h1: ({ children }) => <h1 style={{ color: "red" }}>{children}</h1>,
        MyButton: (props) => <button {...props} />, // MDX 组件
      }}
    >
      <AppContent />
    </MarkdownProvider>
    );
    
    如果未提供 MarkdownProvider,Intlayer 将使用默认的 Markdown 转 JSX 解析器渲染 markdown。

    您还可以使用 .use() 方法提供特定节点的局部覆盖:

    tsx
    {myMarkdownContent.use({
    h1: ({ children }) => <h1 style={{ color: "red" }}>{children}</h1>,
    })}
    

    您可以以字符串形式检索 Markdown:

    tsx
    {myMarkdownContent.value}
    {String(myMarkdownContent)}
    {myMarkdownContent.toString()}
    

    您可以像这样访问您的 markdown 元数据:

    tsx
    {myMarkdownContent.metadata}
    {myMarkdownContent.metadata.title}
    

    Solid 直接支持 JSX 中的 Markdown 节点。

    App.tsx
    import { useIntlayer } from "solid-intlayer";
    import { MarkdownProvider } from "solid-intlayer/markdown";
    
    const AppContent = () => {
    const { myMarkdownContent } = useIntlayer("app");
    return <div>{myMarkdownContent}</div>;
    };
    
    const App = () => (
    <MarkdownProvider
      components={{
        h1: (props) => <h1 style={{ color: "red" }}>{props.children}</h1>,
        MyButton: (props) => <button {...props} />, // MDX 组件
      }}
    >
      <AppContent />
    </MarkdownProvider>
    );
    
    如果未提供 MarkdownProvider,Intlayer 将使用默认的 Markdown 转 JSX 解析器渲染 markdown。

    您还可以使用 .use() 方法提供特定节点的局部覆盖:

    tsx
    {myMarkdownContent.use({
    h1: (props) => <h1 style={{ color: "red" }}>{props.children}</h1>,
    })}
    

    您可以以字符串形式检索 Markdown:

    tsx
    {myMarkdownContent.value}
    {String(myMarkdownContent)}
    {myMarkdownContent.toString()}
    

    您可以像这样访问您的 markdown 元数据:

    tsx
    {myMarkdownContent.metadata}
    {myMarkdownContent.metadata.title}
    

    Angular 使用 [innerHTML] 指令来呈现 Markdown 内容。

    app.component.ts
    import { Component } from "@angular/core";
    import { useIntlayer } from "angular-intlayer";
    
    @Component({
    selector: "app-root",
    template: `<div [innerHTML]="content().myMarkdownContent"></div>`,
    })
    export class AppComponent {
    content = useIntlayer("app");
    }
    
    如果未配置 IntlayerMarkdown 提供程序,Intlayer 将使用默认编译器进行渲染。

    您还可以使用 .use() 方法提供特定节点的局部覆盖:

    typescript
    content().myMarkdownContent.use({
    h1: { class: "text-3xl font-bold" },
    })
    

    您可以以字符串形式检索 Markdown:

    typescript
    content().myMarkdownContent.value
    String(content().myMarkdownContent)
    content().myMarkdownContent.toString()
    

    您可以像这样访问您的 markdown 元数据:

    typescript
    content().myMarkdownContent.metadata
    content().myMarkdownContent.metadata.title
    

    在 Remix 3 中,Markdown 节点在服务器端渲染为 HTML 字符串。使用 Remix JSX 的 innerHTML 属性注入它,或在 html-template 视图中使用 html.raw

    src/views/home.tsx
    import { useIntlayer } from "remix-intlayer";
    
    export const HomePage = () => () => {
    const { myMarkdownContent } = useIntlayer("app");
    
    return <div innerHTML={myMarkdownContent.value} />;
    };
    
    src/views/home.ts
    import { html } from "remix/html-template";
    import { useIntlayer } from "remix-intlayer";
    
    export const renderHomePage = () => {
    const { myMarkdownContent } = useIntlayer("app");
    
    return html.raw`<div>${myMarkdownContent.value}</div>`;
    };
    
    Remix 默认会转义插值。innerHTMLhtml.raw 是两个例外选项,这正是渲染后的 Markdown 字符串所需要的。

    你还可以使用 .use() 方法为特定标签提供本地覆盖。覆盖是返回 HTML 字符串的函数:

    tsx
    <div
    innerHTML={myMarkdownContent.use({
      h1: ({ children }) => `<h1 class="text-3xl font-bold">${children}</h1>`,
    })}
    />
    

    .value 是渲染后的 HTML 字符串,而 String() / .toString() 返回原始 Markdown 源码:

    tsx
    myMarkdownContent.value // "<h1>…</h1>"
    String(myMarkdownContent) // "# …"
    myMarkdownContent.toString() // "# …"
    

    你还可以像这样访问 Markdown 元数据:

    tsx
    myMarkdownContent.metadata
    myMarkdownContent.metadata.title
    

    在 Astro 中,Markdown 节点渲染为 HTML 字符串。使用模板中的 set:html 指令注入它,或在客户端 <script> 中使用 innerHTML

    src/pages/index.astro
    ---
    import { useIntlayer } from "astro-intlayer";
    
    const { myMarkdownContent } = useIntlayer("app");
    ---
    
    <div set:html={myMarkdownContent.value} />
    
    src/components/Content.astro
    <div id="content"></div>
    
    <script>
    import { useIntlayer } from "astro-intlayer";
    
    const { myMarkdownContent } = useIntlayer("app");
    
    document.querySelector("#content")!.innerHTML = myMarkdownContent.value;
    </script>
    
    Astro 默认会转义 {表达式}set:html 是例外选项,这正是渲染后的 Markdown 字符串所需要的。

    你还可以使用 .use() 方法为特定标签提供本地覆盖。覆盖是返回 HTML 字符串的函数:

    astro
    <div
    set:html={myMarkdownContent.use({
      h1: ({ children }) => `<h1 class="text-3xl font-bold">${children}</h1>`,
    })}
    />
    

    .value 是渲染后的 HTML 字符串,而 String() / .toString() 返回原始 Markdown 源码:

    astro
    myMarkdownContent.value // "<h1>…</h1>"
    String(myMarkdownContent) // "# …"
    myMarkdownContent.toString() // "# …"
    

    你还可以像这样访问 Markdown 元数据:

    astro
    myMarkdownContent.metadata
    myMarkdownContent.metadata.title
    

    2. 辅助实用程序(仅限 Markdown 字符串)

    这些实用程序渲染 仅限原始 Markdown 字符串 且独立于 useIntlayer。当您需要从词典以外的来源渲染 Markdown 时使用它们。

    <MarkdownRenderer /> 组件

    使用特定选项渲染 Markdown 字符串。

    tsx
    import { MarkdownRenderer } from "react-intlayer/markdown";
    
    <MarkdownRenderer forceBlock={true} tagfilter={true}>
    {"# 我的标题"}
    </MarkdownRenderer>
    

    useMarkdownRenderer() 钩子

    获取预配置的渲染器函数。

    tsx
    import { useMarkdownRenderer } from "react-intlayer/markdown";
    
    const renderMarkdown = useMarkdownRenderer({
    forceBlock: true,
    components: { h1: (props) => <h1 {...props} className="custom" /> }
    });
    
    return renderMarkdown("# 我的标题");
    

    renderMarkdown() 实用程序

    组件外部渲染的独立实用程序。

    tsx
    import { renderMarkdown } from "react-intlayer/markdown";
    
    const jsx = renderMarkdown("# 我的标题", { forceBlock: true });
    

    <MarkdownRenderer /> 组件

    使用特定选项渲染 Markdown 字符串。

    tsx
    import { MarkdownRenderer } from "next-intlayer/markdown";
    
    <MarkdownRenderer forceBlock={true} tagfilter={true}>
    {"# 我的标题"}
    </MarkdownRenderer>
    

    useMarkdownRenderer() 钩子

    获取预配置的渲染器函数。

    tsx
    import { useMarkdownRenderer } from "next-intlayer/markdown";
    
    const renderMarkdown = useMarkdownRenderer({
    forceBlock: true,
    components: { h1: (props) => <h1 {...props} className="custom" /> }
    });
    
    return renderMarkdown("# 我的标题");
    

    renderMarkdown() 实用程序

    组件外部渲染的独立实用程序。

    tsx
    import { renderMarkdown } from "next-intlayer/markdown";
    
    const jsx = renderMarkdown("# 我的标题", { forceBlock: true });
    

    <MarkdownRenderer /> 组件

    vue
    <script setup>
    import { MarkdownRenderer } from "vue-intlayer/markdown";
    </script>
    
    <template>
    <MarkdownRenderer :forceBlock="true" content="# 我的标题" />
    </template>
    

    <MarkdownRenderer /> 组件

    svelte
    <script lang="ts">
    import { MarkdownRenderer } from "svelte-intlayer/markdown";
    </script>
    
    <MarkdownRenderer forceBlock={true} value="# 我的标题" />
    

    useMarkdownRenderer() 钩子

    svelte
    <script lang="ts">
    import { useMarkdownRenderer } from "svelte-intlayer/markdown";
    const render = useMarkdownRenderer();
    </script>
    
    {@html render("# 我的标题")}
    

    renderMarkdown() 实用程序

    svelte
    <script lang="ts">
    import { renderMarkdown } from "svelte-intlayer/markdown";
    </script>
    
    {@html renderMarkdown("# 我的标题")}
    

    <MarkdownRenderer /> 组件

    tsx
    import { MarkdownRenderer } from "preact-intlayer/markdown";
    
    <MarkdownRenderer forceBlock={true}>
    {"# 我的标题"}
    </MarkdownRenderer>
    

    useMarkdownRenderer() 钩子

    tsx
    import { useMarkdownRenderer } from "preact-intlayer/markdown";
    
    const render = useMarkdownRenderer();
    
    return <div>{render("# 我的标题")}</div>;
    

    renderMarkdown() 实用程序

    tsx
    import { renderMarkdown } from "preact-intlayer/markdown";
    
    return <div>{renderMarkdown("# 我的标题")}</div>;
    

    <MarkdownRenderer /> 组件

    tsx
    import { MarkdownRenderer } from "solid-intlayer/markdown";
    
    <MarkdownRenderer forceBlock={true}>
    {"# 我的标题"}
    </MarkdownRenderer>
    

    useMarkdownRenderer() 钩子

    tsx
    import { useMarkdownRenderer } from "solid-intlayer/markdown";
    
    const render = useMarkdownRenderer();
    
    return <div>{render("# 我的标题")}</div>;
    

    renderMarkdown() 实用程序

    tsx
    import { renderMarkdown } from "solid-intlayer/markdown";
    
    return <div>{renderMarkdown("# 我的标题")}</div>;
    

    IntlayerMarkdownService 服务

    使用服务呈现 Markdown 字符串。

    typescript
    import { IntlayerMarkdownService } from "angular-intlayer/markdown";
    
    export class MyComponent {
    constructor(private markdownService: IntlayerMarkdownService) {}
    
    renderMarkdown(markdown: string) {
      return this.markdownService.renderMarkdown(markdown);
    }
    }
    

    useMarkdownRenderer() Hook

    获取由 installIntlayerMarkdown() 预配置的渲染器函数。它返回一个 HTML 字符串。

    tsx
    import { useMarkdownRenderer } from "remix-intlayer/markdown";
    
    const renderMarkdown = useMarkdownRenderer({ forceBlock: true });
    
    return <div innerHTML={renderMarkdown("# My Title")} />;
    

    renderMarkdown() 实用工具

    忽略全局配置的独立实用工具。

    tsx
    import { renderMarkdown } from "remix-intlayer/markdown";
    
    const html = renderMarkdown("# My Title", { forceBlock: true });
    

    useMarkdownRenderer() Hook

    获取由 installIntlayerMarkdown() 预配置的渲染器函数。它返回一个 HTML 字符串。

    astro
    ---
    import { useMarkdownRenderer } from "astro-intlayer/markdown";
    
    const renderMarkdown = useMarkdownRenderer({ forceBlock: true });
    ---
    
    <div set:html={renderMarkdown("# My Title")} />
    

    renderMarkdown() 实用工具

    忽略全局配置的独立实用工具。

    astro
    ---
    import { renderMarkdown } from "astro-intlayer/markdown";
    
    const html = renderMarkdown("# My Title", { forceBlock: true });
    ---
    
    <div set:html={html} />
    

    使用 MarkdownProvider 进行全局配置

    MarkdownProvider(或其框架对应的组件)为整个应用程序配置 Markdown 渲染管道。它适用于自动 useIntlayer 渲染和辅助实用程序。此处设置的选项为默认值 — .use() 在节点级别将其覆盖。

    AppProvider.tsx
    import { MarkdownProvider } from "react-intlayer/markdown";
    
    export const AppProvider = ({ children }) => (
    <MarkdownProvider
      components={{
        h1: (props) => <h1 style={{color: 'green'}} {...props} />,
        a: ({ href, ...props }) => <a style={{color: 'red'}} {...props} />,
        MyCustomJSXComponent: (props) => <span style={{color: 'red'}} {...props} />,
      }}
    >
      {children}
    </MarkdownProvider>
    );
    
    支持 MDX — Markdown 内部使用的任何组件名称(例如 <MyCustomJSXComponent />)都会根据 components 映射进行解析。

    您还可以使用自己的 markdown 渲染器:

    AppProvider.tsx
    import { MarkdownProvider } from "react-intlayer/markdown";
    
    export const AppProvider = ({ children }) => (
    <MarkdownProvider
      renderMarkdown={async (md) => {
        // Use dynamic import to reduce the bundle size of your application
        const { renderMarkdown } = await import('react-intlayer/markdown');
        return renderMarkdown(md);
      }}
    >
      {children}
    </MarkdownProvider>
    );
    
    动态导入您的 Markdown 渲染器是减少应用程序Bundle 大小的好方法。
    AppProvider.tsx
    import { MarkdownProvider } from "next-intlayer/markdown";
    
    export const AppProvider = ({ children }) => (
    <MarkdownProvider
      components={{
        h1: (props) => <h1 style={{color: 'green'}} {...props} />,
        a: ({ href, ...props }) => <a style={{color: 'red'}} {...props} />,
        MyCustomJSXComponent: (props) => <span style={{color: 'red'}} {...props} />,
      }}
    >
      {children}
    </MarkdownProvider>
    );
    
    支持 MDX — Markdown 内部使用的任何组件名称(例如 <MyCustomJSXComponent />)都会根据 components 映射进行解析。

    您还可以使用自己的 markdown 渲染器:

    AppProvider.tsx
    import { MarkdownProvider } from "next-intlayer/markdown";
    
    export const AppProvider = ({ children }) => (
    <MarkdownProvider
      renderMarkdown={async (md) => {
        // Use dynamic import to reduce the bundle size of your application
        const { renderMarkdown } = await import('next-intlayer/markdown');
        return renderMarkdown(md);
      }}
    >
      {children}
    </MarkdownProvider>
    );
    
    动态导入您的 Markdown 渲染器是减少应用程序Bundle 大小的好方法。
    main.ts
    import { createApp } from "vue";
    import { intlayer } from "vue-intlayer";
    import { intlayerMarkdown } from "vue-intlayer/markdown";
    import App from "./App.vue";
    
    const app = createApp(App);
    
    app.use(intlayer);
    app.use(intlayerMarkdown, {
    components: {
      h1: (props) =>
      h('h1', { style: { color: 'orange' }, ...props }, props.children),
      ComponentDemo: () => h('div', { style: { background: 'grey' } }, 'DEMO'),
      bold: (props) => h('strong', props),
      code: (props) => h('code', props),
    },
    });
    
    app.mount("#app");
    
    支持 MDX — Markdown 内部使用的任何组件名称(例如 <MyCustomJSXComponent />)都会根据 components 映射进行解析。

    您还可以使用自己的 markdown 渲染器:

    main.ts
    import { createApp } from "vue";
    import { intlayer } from "vue-intlayer";
    import { intlayerMarkdown } from "vue-intlayer/markdown";
    import App from "./App.vue";
    
    const app = createApp(App);
    
    app.use(intlayer);
    app.use(intlayerMarkdown, {
    renderMarkdown: async (md) => {
      const { renderMarkdown } = await import('vue-intlayer/markdown');
      return renderMarkdown(md);
    },
    });
    
    app.mount("#app");
    
    动态导入您的 Markdown 渲染器是减少应用程序Bundle 大小的好方法。
    App.svelte
    <script lang="ts">
    import { MarkdownProvider } from "svelte-intlayer/markdown";
    import MyHeading from "./MyHeading.svelte";
    </script>
    
    <MarkdownProvider
    components={{
      h1: MyHeading,
    }}
    >
    <slot />
    </MarkdownProvider>
    
    支持 MDX — Markdown 内部使用的任何组件名称(例如 <MyCustomJSXComponent />)都会根据 components 映射进行解析。

    您还可以使用自己的 markdown 渲染器:

    App.svelte
    <script lang="ts">
    import { MarkdownProvider } from "svelte-intlayer/markdown";
    </script>
    
    <MarkdownProvider
    renderMarkdown={async (md) => {
      const { renderMarkdown } = await import('svelte-intlayer/markdown');
      return renderMarkdown(md);
    }}
    >
    <slot />
    </MarkdownProvider>
    
    动态导入您的 Markdown 渲染器是减少应用程序Bundle 大小的好方法。
    AppProvider.tsx
    import { MarkdownProvider } from "preact-intlayer/markdown";
    
    export const AppProvider = ({ children }) => (
    <MarkdownProvider
      components={{
        h1: ({ children }) => <h1 className="text-2xl font-bold">{children}</h1>,
      }}
    >
      {children}
    </MarkdownProvider>
    );
    
    支持 MDX — Markdown 内部使用的任何组件名称(例如 <MyCustomJSXComponent />)都会根据 components 映射进行解析。

    您还可以使用自己的 markdown 渲染器:

    AppProvider.tsx
    import { MarkdownProvider } from "preact-intlayer/markdown";
    
    export const AppProvider = ({ children }) => (
    <MarkdownProvider
      renderMarkdown={async (md) => {
        const { renderMarkdown } = await import('preact-intlayer/markdown');
        return renderMarkdown(md);
      }}
    >
      {children}
    </MarkdownProvider>
    );
    
    动态导入您的 Markdown 渲染器是减少应用程序Bundle 大小的好方法。
    AppProvider.tsx
    import { MarkdownProvider } from "solid-intlayer/markdown";
    
    export const AppProvider = (props) => (
    <MarkdownProvider
      components={{
        h1: (props) => <h1 className="text-2xl font-bold">{props.children}</h1>,
      }}
    >
      {props.children}
    </MarkdownProvider>
    );
    
    支持 MDX — Markdown 内部使用的任何组件名称(例如 <MyCustomJSXComponent />)都会根据 components 映射进行解析。

    您还可以使用自己的 markdown 渲染器:

    AppProvider.tsx
    import { MarkdownProvider } from "solid-intlayer/markdown";
    
    export const AppProvider = (props) => (
    <MarkdownProvider
      renderMarkdown={async (md) => {
        const { renderMarkdown } = await import('solid-intlayer/markdown');
        return renderMarkdown(md);
      }}
    >
      {props.children}
    </MarkdownProvider>
    );
    
    动态导入您的 Markdown 渲染器是减少应用程序Bundle 大小的好方法。
    app.config.ts
    import { createIntlayerMarkdownProvider } from "angular-intlayer/markdown";
    
    export const appConfig: ApplicationConfig = {
    providers: [
      createIntlayerMarkdownProvider({
        components: {
          h1: { class: "text-2xl font-bold" },
        },
      }),
    ],
    };
    
    支持 MDX — 任何在 Markdown 中使用的组件名称(例如 <MyCustomJSXComponent />)都会根据 components map 进行解析。

    你也可以使用自己的 markdown 渲染器:

    app.module.ts
    import { NgModule } from '@angular/core';
    import { IntlayerMarkdownModule } from 'angular-intlayer/markdown';
    
    @NgModule({
    imports: [
      IntlayerMarkdownModule.forRoot({
        renderMarkdown: async (md) => {
          const { renderMarkdown } = await import('angular-intlayer/markdown');
          return renderMarkdown(md);
        }
      })
    ]
    })
    export class AppModule {}
    
    动态导入您的 Markdown 渲染器是减少应用程序Bundle 大小的好方法。

    Remix 没有组件树来承载 provider,因此配置在服务器启动时作为单例安装一次。它配置由 useMarkdownRenderer() 返回的渲染器。由 useIntlayer 返回的 md 节点使用默认编译器渲染;通过 .use() 为每个节点覆盖其标签。

    src/router.ts
    import { installIntlayerMarkdown } from "remix-intlayer/markdown";
    
    installIntlayerMarkdown({
    forceBlock: true,
    components: {
      h1: ({ children }) => `<h1 class="text-2xl font-bold">${children}</h1>`,
    },
    });
    

    你也可以使用自己的 markdown 渲染器:

    src/router.ts
    import { installIntlayerMarkdown } from "remix-intlayer/markdown";
    
    installIntlayerMarkdown({
    renderMarkdown: async (md) => {
      const { marked } = await import("marked");
      return marked(md) as string;
    },
    });
    
    使用 installIntlayerMarkdownDynamic(async () => …) 延迟加载渲染器本身;加载器仅在第一次调用时运行。

    Astro 没有组件树来承载 provider,因此配置在中间件(服务器)和客户端 <script>(浏览器)中作为单例安装一次。它配置由 useMarkdownRenderer() 返回的渲染器。由 useIntlayer 返回的 md 节点使用默认编译器渲染;通过 .use() 为每个节点覆盖其标签。

    src/middleware.ts
    import { installIntlayerMarkdown } from "astro-intlayer/markdown";
    import { defineMiddleware } from "astro:middleware";
    
    // 服务器启动时运行一次;Intlayer 中间件本身由集成在此文件之前注册。
    installIntlayerMarkdown({
    forceBlock: true,
    components: {
      h1: ({ children }) => `<h1 class="text-2xl font-bold">${children}</h1>`,
    },
    });
    
    export const onRequest = defineMiddleware((_context, next) => next());
    

    你也可以使用自己的 markdown 渲染器:

    src/middleware.ts
    import { installIntlayerMarkdown } from "astro-intlayer/markdown";
    
    installIntlayerMarkdown({
    renderMarkdown: async (md) => {
      const { marked } = await import("marked");
      return marked(md) as string;
    },
    });
    
    使用 installIntlayerMarkdownDynamic(async () => …) 延迟加载渲染器本身;加载器仅在第一次调用时运行。

    Suspense (悬念)

    Intlayer Markdown渲染器是动态加载的。虽然经过优化,但底层解析器块的大小约为55kb。同步加载会延迟初始页面渲染,并降低首次内容绘制(FCP)。

    为防止阻塞UI,Intlayer集成了React的Suspense API。它在后台获取解析器,并在下载期间抛出Promise。

    将渲染Intlayer Markdown的任何组件包装在<Suspense>边界中。这会在下载块时显示本地化的后备状态,从而允许立即渲染DOM的其余部分。

    警告:如果不提供<Suspense>边界,React将在根级别暂停或阻止整个组件树的渲染,直到55kb的块完全加载完毕。

    在Next.js App Router中,您可以为客户端组件使用React Suspense,或为服务器组件使用loading.tsx文件。

    客户端组件:

    components/MyComponent.tsx
    "use client";
    import { useIntlayer } from "next-intlayer";
    import { Suspense } from "react";
    
    const MyComponent = () => {
      const markdownContent = useIntlayer("my-markdown");
    
      return (
        <Suspense fallback={<div>Loading...</div>}>{markdownContent}</Suspense>
      );
    };
    

    带有loading.tsx的服务器组件:

    app/loading.tsx
    export default function Loading() {
      return <div>Loading...</div>;
    }
    
    app/page.tsx
    import { useIntlayer } from "next-intlayer/server";
    
    const MyPage = () => {
      const markdownContent = useIntlayer("my-markdown");
      return <div>{markdownContent}</div>;
    };
    
    export default MyPage;
    
    tsx
    import { useIntlayer } from "react-intlayer";
    import { Suspense } from "react";
    
    const MyComponent = () => {
      const markdownContent = useIntlayer("my-markdown");
    
      return (
        <Suspense fallback={<div>Loading...</div>}>{markdownContent}</Suspense>
      );
    };
    

    Vue内置了<Suspense>组件。将渲染Markdown内容的组件包装在<Suspense>边界中。

    MyComponent.vue
    <script setup>
    import { useIntlayer } from "vue-intlayer";
    
    const { markdownContent } = useIntlayer("my-markdown");
    </script>
    
    <template>
      <Suspense>
        <component :is="markdownContent" />
        <template #fallback>
          <div>Loading...</div>
        </template>
      </Suspense>
    </template>
    

    Svelte没有Suspense API的等效项。使用{#await}块处理Markdown内容的异步渲染。

    MyComponent.svelte
    <script lang="ts">
    import { useIntlayer } from "svelte-intlayer";
    
    const content = useIntlayer("my-markdown");
    </script>
    
    {#await $content.markdownContent}
      <div>Loading...</div>
    {:then rendered}
      {@html rendered}
    {/await}
    

    Preact通过preact/compat支持React的Suspense API。

    MyComponent.tsx
    import { useIntlayer } from "preact-intlayer";
    import { Suspense } from "preact/compat";
    
    const MyComponent = () => {
      const markdownContent = useIntlayer("my-markdown");
    
      return (
        <Suspense fallback={<div>Loading...</div>}>{markdownContent}</Suspense>
      );
    };
    

    Solid有自己的来自solid-js<Suspense>组件。

    MyComponent.tsx
    import { useIntlayer } from "solid-intlayer";
    import { Suspense } from "solid-js";
    
    const MyComponent = () => {
      const { markdownContent } = useIntlayer("my-markdown");
    
      return (
        <Suspense fallback={<div>Loading...</div>}>{markdownContent}</Suspense>
      );
    };
    

    Angular没有Suspense API。使用Angular的可延迟视图(@defer)来处理延迟加载的Markdown内容(需要Angular 17+)。

    my.component.ts
    import { Component } from "@angular/core";
    import { useIntlayer } from "angular-intlayer";
    
    @Component({
      selector: "app-my",
      template: `
        @defer {
          <div [innerHTML]="content().markdownContent"></div>
        } @loading {
          <div>Loading...</div>
        }
      `,
    })
    export class MyComponent {
      content = useIntlayer("my-markdown");
    }
    

    服务端渲染 (SSR) 与 注水 (Hydration)

    与其他 Markdown 解析器(如 remark / rehype)相比,Intlayer Markdown 无依赖关系,且既可以在客户端运行,也可以在服务端运行。

    但是 Intlayer 针对服务端渲染 (SSR) 框架(如 Next.js App Router, React Router, Nuxt, SvelteKit 等)进行了解析优化。

    Intlayer 允许您在服务端将 Markdown 预解析为抽象语法树 (AST),而不是将原始 Markdown 字符串发送到客户端并在浏览器中解析(这会带来性能损失)。

    您可以在服务端使用框架的 Intlayer 包中的 parseMarkdown 函数来生成可序列化的 AST(ParsedMarkdown 对象),并将其直接传递给前端。所有 Intlayer 渲染工具(如 <MarkdownRenderer>useMarkdownRenderer 等)都会自动接受此 AST 对象并无缝渲染它。

    服务端/客户端架构中的示例

    server.ts
    import { parseMarkdown } from "react-intlayer/markdown";
    
    // 1. 在服务端:将 markdown 解析为可序列化的 AST
    export const loader = async () => {
    const markdownString = "## My title \n\nLorem Ipsum";
    const ast = parseMarkdown(markdownString);
    
    // 将 AST 作为 JSON 返回给客户端
    return Response.json({ content: ast });
    };
    
    client.tsx
    import { useLoaderData } from "react-router";
    import { MarkdownRenderer } from "react-intlayer/markdown";
    
    // 2. 在客户端:直接渲染 AST,无需重新解析
    export default function Page() {
    const { content } = useLoaderData();
    
    // 渲染器既接受原始字符串,也接受解析后的 AST
    return <MarkdownRenderer content={content} />;
    }
    
    app/page.tsx
    import { parseMarkdown } from "next-intlayer/markdown";
    import { MarkdownRenderer } from "next-intlayer/markdown";
    
    export default async function Page() {
    // 1. 在服务端将 markdown 解析为可序列化的 AST
    const markdownString = "## My title \n\nLorem Ipsum";
    const ast = parseMarkdown(markdownString);
    
    // 2. 直接渲染 AST
    // 在服务端组件(Server Component)中,这可以无缝工作,并在需要时
    // 直接将 AST 传递给底层的客户端组件。
    return <MarkdownRenderer content={ast} />;
    }
    
    pages/index.vue
    <script setup lang="ts">
    import { parseMarkdown } from "vue-intlayer/markdown";
    import { MarkdownRenderer } from "vue-intlayer/markdown";
    
    // 1. 在服务端获取并解析 markdown 为 AST
    const { data: ast } = await useAsyncData('markdown', () => {
    const markdownString = "## My title \n\nLorem Ipsum";
    return parseMarkdown(markdownString);
    });
    </script>
    
    <template>
    <!-- 2. 在客户端:直接渲染 AST,无需重新解析 -->
    <MarkdownRenderer :content="ast" />
    </template>
    
    +page.server.ts
    import { parseMarkdown } from "svelte-intlayer/markdown";
    
    // 1. 在服务端:将 markdown 解析为可序列化的 AST
    export const load = async () => {
    const markdownString = "## My title \n\nLorem Ipsum";
    const ast = parseMarkdown(markdownString);
    
    // 将 AST 返回给客户端
    return { content: ast };
    };
    
    +page.svelte
    <script lang="ts">
    import { MarkdownRenderer } from "svelte-intlayer/markdown";
    export let data;
    </script>
    
    <!-- 2. 在客户端:直接渲染 AST,无需重新解析 -->
    <MarkdownRenderer value={data.content} />
    

    Angular SSR 通常在初始加载期间在服务端解析数据,并在客户端进行注水。您可以使用解析器(resolvers)来传递 AST。

    app.resolver.ts
    import { Injectable } from "@angular/core";
    import { Resolve } from "@angular/router";
    import { parseMarkdown, type ParsedMarkdown } from "angular-intlayer/markdown";
    
    @Injectable({ providedIn: "root" })
    export class MarkdownResolver implements Resolve<ParsedMarkdown> {
    resolve(): ParsedMarkdown {
      const markdownString = "## My title \n\nLorem Ipsum";
      // 1. 在服务端:将 markdown 解析为可序列化的 AST
      return parseMarkdown(markdownString);
    }
    }
    
    app.component.ts
    import { Component } from "@angular/core";
    import { ActivatedRoute } from "@angular/router";
    import { IntlayerMarkdownService, type ParsedMarkdown } from "angular-intlayer/markdown";
    
    @Component({
    selector: "app-root",
    template: `<div [innerHTML]="renderedMarkdown"></div>`,
    })
    export class AppComponent {
    renderedMarkdown: string = "";
    
    constructor(
      private route: ActivatedRoute,
      private markdownService: IntlayerMarkdownService
    ) {
      // 2. 在客户端:直接渲染 AST,无需重新解析
      this.route.data.subscribe((data) => {
        this.renderedMarkdown = this.markdownService.renderMarkdown(
          data.markdownAst
        ) as string;
      });
    }
    }
    

    Remix 3 在服务器端渲染并流式传输 HTML,因此无需将 AST 传递给客户端。解析一次,并在构建页面的任何位置渲染 AST:

    src/views/article.tsx
    import { parseMarkdown, renderMarkdown } from "remix-intlayer/markdown";
    
    // 1. 将 Markdown 解析为可序列化的 AST(例如在模块加载时解析一次)
    const ast = parseMarkdown("## My title \n\nLorem Ipsum");
    
    export const ArticlePage = () => () => (
    // 2. 渲染 AST:渲染器接受原始字符串或解析后的 AST
    <article innerHTML={renderMarkdown(ast)} />
    );
    

    Astro 页面在服务器端渲染,因此无需将 AST 传递给客户端。在 frontmatter 中解析 Markdown 并使用 set:html 渲染:

    src/pages/article.astro
    ---
    import { parseMarkdown, renderMarkdown } from "astro-intlayer/markdown";
    
    // 1. 将 Markdown 解析为可序列化的 AST
    const ast = parseMarkdown("## My title \n\nLorem Ipsum");
    ---
    
    <!-- 2. 渲染 AST:渲染器接受原始字符串或解析后的 AST -->
    <article set:html={renderMarkdown(ast)} />
    

    这种模式确保 Markdown 解析逻辑完全在服务端执行,从而显著减少客户端执行时间,并提高初始注水速度。

    选项参考

    这些选项可传递给MarkdownProviderMarkdownRendereruseMarkdownRendererrenderMarkdown

    OptionTypeDefault描述
    forceBlockbooleanfalse强制将输出包装在块级元素(例如<div>)中。
    forceInlinebooleanfalse强制将输出包装在内联元素(例如<span>)中。
    tagfilterbooleantrue启用GitHub标签过滤器以通过剥离危险的HTML标签来提高安全性。
    preserveFrontmatterbooleanfalse如果为true,则不会剥离Markdown字符串开头的frontmatter。
    componentsOverrides{}HTML标签到自定义组件的映射(例如{ h1: MyHeading })。
    wrapperComponentnull用于包装渲染Markdown的自定义组件。
    renderMarkdownFunctionnull自定义渲染函数,用于完全替换默认的Markdown编译器。