第 09 / 11 章

代码块与 MDX 组件

mdx.tsx 如何映射标签,以及 code-block.tsx 如何做多文件 Tab、复制与沙箱预览。

⏱ 2 分钟534 字

components/mdx/index.tsx:标签映射§

把原生标签换成站点组件:

Markdown / MDX组件行为
h2/h3/h4保留 id,右侧加 § 锚点链接
a外链自动 target="_blank" + rel="noopener noreferrer"
pre整块换成 CodeBlock(客户端)
code有 language-* 类名时原样输出(给代码块用);否则做成行内代码样式
table外包一层横向滚动容器
CalloutMDX 里可直接写的提示框(tip / note / warn)

这里有一个容易忽略的细节:pre 映射成客户端组件后,高亮后的 <code> 子节点仍作为 children 传入;CodeBlock 用 childrenArr[activeIdx] 显示当前 Tab 的高亮结果,同时用 data-files 里的原文做复制和预览。

components/mdx/code-block.tsx:交互最重的一块§

文件约 460 行,职责可以拆成四段。

1. 解析 props§

从 data-files JSON 解析文件列表;data-preview === "true" 时尝试构建预览文档。

2. buildPreviewDoc§

  • 过滤出可预览语言
  • 找 html(Prism 里叫 markup)、拼接所有 css 为 <style>、所有 js 为 <script>
  • 若有完整 html,则注入到 </head> / </body> 前;否则生成一份默认深色底的壳页面
  • 对 </style> / </script> 做字符串拆开拼接,降低源码里误闭合标签的风险

3. UI:预览区 + Tab + 行号 + 复制§

  • 预览:iframe + sandbox="allow-scripts allow-forms allow-modals allow-popups"(故意没有 allow-same-origin,降低沙箱逃逸面)
  • 重跑:setFrameKey(k => k + 1) 强制重挂 iframe
  • Tab:方向键 / Home / End;复制只复制当前文件原文
  • 行号:按当前文件源码行数生成左侧 gutter

4. 文件类型图标§

根据扩展名渲染小徽章或 React 风格 SVG,纯展示,不影响逻辑。

可运行示例(三件套会合成独立文档):

预览
<button id="go">点亮</button>

data-files / 合并 / 高亮如何在服务端写进 AST,见本册「rehype 源码」章。更细的作者语法说明,见散篇里关于 MDX 代码块的专题文。下一章讲阅读进度子系统。