You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SSR框架的loader中传递MDX编译后的组件?

如何在SSR框架的loader中传递MDX编译后的组件?

我完全理解你的痛点——既然用了SSR框架,肯定希望在服务端就把MDX编译成可用的React组件,而不是退回到客户端异步加载,这完全浪费了SSR的优势。咱们来一步步解决这个问题:

问题根源

你当前用的run函数(来自@mdx-js/runtime)本质上是为客户端运行时设计的,在Node.js环境(SSR的loader一般跑在Node端)中,它无法正确生成可直接渲染的React组件函数,反而返回一个对象,导致你只能在客户端通过useEffect处理。

解决方案:在Loader中直接编译并生成React组件

我们可以绕过客户端的run函数,直接在服务端把MDX编译后的代码转换成可执行的React组件,具体步骤如下:

1. 调整MDX编译配置

首先修改你的MDX编译逻辑,确保输出的代码兼容Node.js环境,同时指定React相关的JSX配置:

return {
  data: result,
  body: String(
    await compile(item, {
      outputFormat: "function-body",
      jsxRuntime: "automatic", // 自动引入React JSX运行时
      jsxImportSource: "react", // 指定React作为JSX源
    })
  ),
};

2. 在Loader中执行编译后的代码生成组件

我们可以用eval(或更安全的Node.js vm模块)来执行MDX编译后的代码,同时注入React和MDX相关的依赖:

import React from 'react';
import { useMDXComponents } from '@mdx-js/react';

export async function loader({ params }: Route.LoaderArgs) {
  const post = await getBlogPost(params.slug);

  if (post?.body) {
    // 把MDX的function-body包裹成完整的React组件代码
    const componentCode = `
import React from 'react';
import { useMDXComponents } from '@mdx-js/react';

export default function MDXContent(props) {
  const components = useMDXComponents(props.components);
  return ${post.body};
}
    `;

    // 创建一个模拟的模块环境来执行代码
    const module = { exports: {} };
    const require = (moduleName) => {
      switch(moduleName) {
        case 'react': return React;
        case '@mdx-js/react': return { useMDXComponents };
        default: throw new Error(`Unsupported module: ${moduleName}`);
      }
    };

    // 执行代码(仅当你信任MDX内容时使用,避免用户输入的恶意代码)
    eval(`(function(require, module, exports) { ${componentCode} })(require, module, module.exports)`);
    
    const Content = module.exports.default;

    return {
      ...post.data,
      Content, // 直接传递编译好的组件
    };
  }

  throw redirect("/404");
}

3. 在组件中直接使用服务端传递的组件

现在你可以在前端组件里直接渲染服务端传来的Content组件,完全不需要客户端异步处理:

export default function Entry({ loaderData }: Route.ComponentProps) {
  const { Content } = loaderData;
  return <div><Content /></div>;
}

安全提醒

  • 如果你是处理自己可控的内容(比如个人博客),用eval完全没问题;
  • 如果MDX内容来自用户输入,一定要先做严格的内容 sanitize,避免XSS攻击,或者考虑使用更安全的vm.createContext来隔离执行环境。

替代方案:使用框架专属的MDX工具

如果你用的是Remix、Next.js这类特定SSR框架,它们有专门的MDX处理方案:

  • Remix可以用@remix-run/mdx,直接在loader中导入或编译MDX;
  • Next.js支持next-mdx-remote,专门处理远程获取的MDX内容,完美适配SSR。

备注:内容来源于stack exchange,提问作者Tomek Buszewski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 18:10:29