@mdx-js/vue 完全指南:在 Vue 项目中用 Context 为 MDX 注入组件
【免费下载链接】mdxMarkdown for the component era项目地址: https://gitcode.com/gh_mirrors/md/mdx
@mdx-js/vue是 MDX 官方生态中面向 Vue 的 context 组件提供器,它基于 Vue 的provide/inject机制,让嵌套的 MDX 文件无需层层手动传递components属性即可共享自定义组件映射。读完本文,你将掌握@mdx-js/vue的安装方式、MDXProvider与useMDXComponents两个核心 API 的用法、providerImportSource编译选项的底层实现原理,以及如何在 Vue 3 项目中正确配置并验证这套组件注入体系。
什么是 @mdx-js/vue
@mdx-js/vue是一个基于context(上下文)的组件提供器,作用是把 Vue 与 MDX 结合起来。它对外只提供两个标识符:MDXProvider(Vue 组件)和useMDXComponents(组合式函数),且没有默认导出——这一点在 packages/vue/test/index.js 的公开 API 测试中有明确验证:
assert.deepEqual(Object.keys(await import('@mdx-js/vue')).sort(), [ 'MDXProvider', 'useMDXComponents' ])整个包的实际实现非常精简,核心代码全部位于 packages/vue/lib/index.js,对外入口 packages/vue/index.js 仅做了一行再导出:
export {MDXProvider, useMDXComponents} from './lib/index.js'什么时候需要它:为什么官方说"不是必需"
官方文档给出的第一条重要提示是:这个包并不是 MDX 在 Vue 中运行的必要条件。单层使用时,直接给 MDX 组件传components属性即可完成组件替换,引入 Provider 只会徒增包体。
但在嵌套 MDX 文件(在 MDX 中再 import 其他 MDX/Markdown 文件)的场景下,手动透传会变得繁琐——你不得不在每个子组件上写<License components={props.components} />这样的重复代码。针对这一痛点,使用 MDX 指南中给出了三步设置方案:
- 根据所用框架安装
@mdx-js/react、@mdx-js/preact或@mdx-js/vue; - 在 MDX 编译器的
ProcessorOptions中把providerImportSource配置为对应包名(Vue 场景即'@mdx-js/vue'); - 从该包导入
MDXProvider,用它包裹最顶层的 MDX 内容组件并传入components。
Context 的职责正如官方文档所述:提供一种在组件树中传递数据、而无需在每一层手动透传 props 的方式。这正是MDXProvider存在的价值。
安装
@mdx-js/vue是ESM only的包,不支持 CommonJS。根据 packages/vue/package.json 的声明:
- 当前版本为
3.1.1,"type": "module","exports": "./index.js"; peerDependencies要求vue >= 3.0.0(依赖的是 Vue 3 的组合式 API 与 Fragment 渲染);- 运行时依赖仅
@types/mdx ^2.0.0(用于提供MDXComponents类型),并标记"sideEffects": false,方便打包器摇树优化。
Node.js(版本 16+)配合 npm 安装:
npm install @mdx-js/vue在 Deno 中使用 esm.sh:
import {MDXProvider} from 'https://esm.sh/@mdx-js/vue@3'在浏览器中直接以模块方式使用 esm.sh:
<script type="module"> import {MDXProvider} from 'https://esm.sh/@mdx-js/vue@3?bundle' </script>?bundle参数用于让 esm.sh 返回一个适合浏览器直接执行的打包产物。
基本使用
下面是一个完整的 Vue 3 使用示例(摘自官方文档):假设你通过@mdx-js/esbuild、@mdx-js/loader、@mdx-js/node-loader或@mdx-js/rollup等集成工具把./post.mdx编译为 JS,并且编译配置中设置了options.providerImportSource: '@mdx-js/vue'。
import {MDXProvider} from '@mdx-js/vue' import {createApp} from 'vue' import Post from './post.mdx' // ^-- 假设已用某个集成工具将 MDX 编译为 JS,且配置了 // `options.providerImportSource: '@mdx-js/vue'` createApp({ data() { return {components: {h1: 'h2'}} }, template: '<MDXProvider v-bind:components="components"><Post /></MDXProvider>', components: {MDXProvider, Post} })注意v-bind:components="components":components数据在data()中定义,MDX 编译产物中的# Hello world会在渲染时通过 Provider 上下文找到h1的替代组件,最终被渲染成<h2>。
官方特别提醒:你完全可以不用MDXProvider,直接把components作为属性传给 MDX 组件。对于单个 MDX 文件,更简洁的等价写法是:
-createApp({ - data() { - return {components: {h1: 'h2'}} - }, - template: '<MDXProvider v-bind:components="components"><Post /></MDXProvider>', - components: {MDXProvider, Post} -}) +createApp(Post, {components: {h1: 'h2'}})如何开始使用 MDX 与 Vue,可参考入门指南;Provider 的完整设计动机与适用场景,可参考使用 MDX 指南中的 MDX provider 章节。
API 详解
本包导出MDXProvider和useMDXComponents两个标识符,没有默认导出。
MDXProvider(properties?)
MDX 上下文的提供器,类型为 Vue 的Component。它接收一个可选的components属性(MDXComponents类型),并在渲染时把传入的组件通过 Vue 的provide注入到整棵子树中。
useMDXComponents(components?)
从 MDX Context 中读取当前组件映射。
- 参数:没有参数(文档标题中的
(components?)仅为历史遗留写法,源码签名useMDXComponents()不接受任何入参); - 返回值:当前的组件映射,类型为
MDXComponents(该类型来自mdx/types.js,由@types/mdx提供)。
Props
MDXProvider的 TypeScript 配置类型,包含一个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
components | MDXComponents(可选) | 需要注入的额外组件 |
工作原理:provide/inject 与 providerImportSource
从源码层面看,MDXProvider的实现极简(见 packages/vue/lib/index.js):
import {Fragment, createVNode, inject, provide} from 'vue' export const MDXProvider = { name: 'MDXProvider', props: { components: { default() { return {} }, type: Object } }, setup(properties) { provide('$mdxComponents', properties.components) }, render() { return createVNode( Fragment, undefined, this.$slots.default ? this.$slots.default() : [] ) } } export function useMDXComponents() { return inject('$mdxComponents', {}) }几个值得注意的实现细节:
- 上下文键:
setup中用provide('$mdxComponents', ...)注入,useMDXComponents用inject('$mdxComponents', {})读取,默认值为空对象,保证未提供 Provider 时也能安全运行。 - 透传渲染:
render()返回一个Fragment包裹的默认插槽内容,因此MDXProvider本身不产生多余 DOM 节点,可以任意包裹 MDX 内容组件。 - 懒读取:
useMDXComponents只有在被调用时才从上下文中取值,这为 MDX 编译产物的"运行时按需注入组件"提供了钩子。
编译侧的配合机制位于 packages/mdx/lib/plugin/recma-jsx-rewrite.js。当providerImportSource被设置时,该插件会在编译产物中:
- 以
useMDXComponents为导入名、_provideComponents为本地别名,从providerImportSource指向的模块插入 import 语句(对应源码中createImportProvider函数); - 在
_createMdxContent函数内调用_provideComponents()获取上下文中的组件,再与props.components及局部定义组件做合并(对应parameters.push({... _provideComponents ...})及后续的ObjectExpression合并逻辑); - 合并结果赋给
_components,并从中解构出 MDX 中实际用到的组件名(如const {MyComponent, wrapper: MDXLayout} = _components)。
也就是说,providerImportSource的值本身"是什么"并不重要,关键是该模块必须导出一个名为useMDXComponents的标识符——编译插件只认这个名字。@mdx-js/vue恰好满足这一约定,因此可以直接作为该选项的值。
如果不想走编译期注入,也可以使用@mdx-js/mdx的evaluate在运行时编译并执行 MDX:配置providerImportSource: '#'并在run的 options 中传入useMDXComponents(见 packages/mdx/lib/util/resolve-evaluate-options.js)。需要说明的是,run/runSync内部通过new Function求值执行编译产物(见 packages/mdx/lib/run.js),官方明确标注了"这会 eval JavaScript"的风险警示,因此只应运行可信内容。
测试验证
packages/vue/test/index.js 用 Node 内置的node:test框架对本包做了全面覆盖,核心断言包括:
- 公开 API 仅导出
MDXProvider与useMDXComponents两个标识符; - 通过
compile+run评估 MDX(# hi→<h1>hi</h1>),验证 MDX 内容本身能被 Vue 正确渲染; - 支持在 MDX 内定义 Vue 组件(
export const A = {render() {...}}后以<A />使用); - 支持直接传
components({components: {h1: 'h2'}}渲染出<h2>hi</h2>); - 支持
MDXProvider包裹(含带components、不带components、无插槽内容三种形态,无内容时渲染为空字符串'')。
测试通过@vue/server-renderer的renderToString得到 HTML 字符串后去除 SSR 注释再做断言,说明该包同时适用于客户端渲染与服务端渲染场景。
TypeScript 类型支持
@mdx-js/vue完全使用 TypeScript 编写类型,并额外导出Props类型。要获得完整类型提示,需要确保 TypeScript 的JSX命名空间已正确配置——通常通过安装并引入框架自身的类型(Vue 场景即vue包自带类型)来完成。编译期providerImportSource的类型校验与MDXComponents的定义由@types/mdx提供,这也是该包唯一的运行时依赖。
兼容性
unified 社区维护的项目遵循"与仍在维护的 Node.js 版本保持兼容"的策略;每发布一个 major 版本,就会放弃对已停止维护的 Node 版本的支持。因此当前发布线@mdx-js/vue@^3的目标是兼容Node.js 16+,并保持 ESM only 的模块形态。安装或升级前请确认你的运行时满足上述前提。
安全说明
关于 MDX 内容与组件注入的安全边界,官方在安全章节中有统一说明。结合本包的实际行为,需要特别记住:MDX 支持嵌入 JSX 表达式和组件,配合run/runSync这类求值机制时,只应编译和执行可信来源的内容,否则可能带来任意代码执行风险。
许可证
@mdx-js/vue以 MIT 协议发布,版权归 Compositor 与 Vercel 所有。
【免费下载链接】mdxMarkdown for the component era项目地址: https://gitcode.com/gh_mirrors/md/mdx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考