如何在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
相关产品推荐
相关产品推荐

