如何在Astro MDX中向JSX组件传递带缩进多行代码并实现多语言切换?
解决方案
1. 约定代码块分组规则
在MDX里,给需要切换的多语言代码块添加分组标识,直接在代码块的meta部分写group=xxx即可,示例:
// React 实现 <button onClick={() => alert('Hello')}>点我</button>
<!-- Vue 实现 --> <button @click="alert('Hello')">点我</button>
<!-- Svelte 实现 --> <button on:click={() => alert('Hello')}>点我</button>
2. 编写Rehype插件合并同组代码块
创建src/plugins/rehype-code-groups.js,这个插件会遍历MDX的AST结构,收集同组代码块并替换为自定义组件节点:
import { visit } from 'unist-util-visit'; export default function rehypeCodeGroups() { return async (tree) => { const groups = new Map(); // 收集所有带group标识的代码块 visit(tree, 'element', (node, index, parent) => { if (node.tagName === 'pre' && node.children[0]?.tagName === 'code') { const codeNode = node.children[0]; const groupId = codeNode.data?.meta?.match(/group=(\w+)/)?.[1]; if (!groupId) return; // 提取语言类型和代码内容 const lang = codeNode.properties.className?.find(c => c.startsWith('language-'))?.replace('language-', '') || 'plaintext'; const code = codeNode.children[0].value; // 按分组归类代码 if (!groups.has(groupId)) groups.set(groupId, []); groups.get(groupId).push({ lang, code }); // 移除原代码块节点 parent.children.splice(index, 1); index--; } }); // 将分组后的代码块插入回AST visit(tree, 'root', (node) => { groups.forEach((codes) => { node.children.push({ type: 'mdxJsxFlowElement', name: 'CodeBlockGroup', attributes: [ { type: 'mdxJsxAttribute', name: 'codes', value: JSON.stringify(codes) } ], children: [] }); }); }); }; }
如果需要代码高亮,可集成shiki在插件中提前处理:
// 插件顶部引入shiki import { getHighlighter } from 'shiki'; const highlighter = await getHighlighter({ themes: ['github-light'], langs: ['javascript', 'vue', 'svelte', 'jsx'] }); // 收集代码时替换为高亮后的HTML const highlighted = highlighter.codeToHtml(code, { lang }); // 把{ lang, code, highlighted }存入分组
3. 在Astro配置中启用插件
修改astro.config.mjs,将自定义插件加入MDX的rehype插件列表:
import { defineConfig } from 'astro/config'; import mdx from '@astrojs/mdx'; import rehypeCodeGroups from './src/plugins/rehype-code-groups.js'; export default defineConfig({ integrations: [mdx()], markdown: { rehypePlugins: [rehypeCodeGroups], }, });
4. 实现多语言切换组件
创建src/components/CodeBlockGroup.astro,处理切换逻辑和样式:
--- const { codes } = Astro.props; const codeList = JSON.parse(codes); let activeLang = codeList[0].lang; --- <div class="code-group"> <!-- 切换标签栏 --> <div class="code-tabs"> {codeList.map((item) => ( <button class:list={['tab-btn', activeLang === item.lang ? 'active' : '']} onClick={() => (activeLang = item.lang)} > {item.lang} </button> ))} </div> <!-- 代码展示区 --> <pre class="code-block"> <code set:html={item.highlighted || item.code}></code> </pre> </div> <style> .code-group { margin: 1.5rem 0; border-radius: 8px; overflow: hidden; border: 1px solid #e5e7eb; } .code-tabs { display: flex; background: #f3f4f6; } .tab-btn { padding: 0.5rem 1rem; border: none; background: transparent; cursor: pointer; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; } .tab-btn.active { color: #1f2937; border-bottom-color: #3b82f6; background: white; } .code-block { margin: 0; padding: 1rem; background: white; overflow-x: auto; } </style>
5. 全局注册组件
在src/components/MDXComponents.astro中注册组件,确保所有MDX文件可直接使用:
--- import CodeBlockGroup from './CodeBlockGroup.astro'; --- <CodeBlockGroup /> export default { CodeBlockGroup, };
然后在astro.config.mjs的MDX配置中指定全局组件路径:
integrations: [mdx({ components: './src/components/MDXComponents.astro', })],
这样就能在MDX中用原生三重反引号语法编写代码块,仅需添加group标识即可自动生成多语言切换的代码块组件。
内容的提问来源于stack exchange,提问作者Ashwin Prasad
相关产品推荐
相关产品推荐

