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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 04:43:14