Markdown 转 JSX 实战:把文档塞进 React 页面的正确做法
把 Markdown 转成 React 组件,class 改 className、标签全部闭合,本地浏览器完成不上传。讲清楚和 dangerouslySetInnerHTML 的区别,以及什么时候该用构建时转换。
Markdown 转 JSX 实战:把文档塞进 React 页面的正确做法
我手里经常有一段现成的 Markdown:组件库的 README、某次发布的更新日志、助手吐出来的一段说明。它们都需要变成 React 页面里的真实内容。手工改的话,第一件烦人事就是 class 要改成 className,<img> <br> 这类标签必须自闭合,否则 JSX 编译器当场报错。一段几百字的文档逐行改下来,十分钟就没了。把这件事交给 Markdown 转 JSX 工具 之后,我基本不再手动收尾。
为什么不直接用运行时 Markdown 库
最常见的做法是引一个 react-markdown 或 marked,在组件里运行时渲染。问题在于:这会把一个完整的解析器打进你的包里,而且每次渲染、每次加载页面,都在重新解析同一段固定的文本。
如果这段 Markdown 根本不变(文档页、营销块、发布说明),那解析它一万次的结果都一样。一次性转成 JSX,意味着包里零解析器、运行时零解析开销,内容还是能直接上样式、接 props、拆组件的真实元素。判断标准很简单:源文本随请求变化(用户产生的、网络拉来的),用运行时渲染器;源文本固定,用构建时转换。
class 转 className,标签必须闭合
这是 Markdown 转 JSX 最容易踩的两个坑,也是这个工具帮你兜底的核心。
JSX 不是 HTML。在 JSX 里写 class="x" 会被 React 警告并忽略,正确写法是 className="x";for 要写成 htmlFor。更要命的是闭合规则:HTML 里 <img src="a.png"> 不写结尾斜杠没问题,但 JSX 要求每个标签都闭合,<img /> <br /> 这种 void 元素少一个斜杠就编译失败。
转换器会把这些全部处理掉:class 改 className、for 改 htmlFor、img br 补自闭合、围栏代码块的 language-js 类名正确改写、HTML 注释转成 JSX 注释。多个顶层块还会自动套一层 Fragment,因为 JSX 要求单一根节点。
看一个真实的输入输出
输入这段 Markdown:
## 安装
用你的包管理器安装:
npm install toolora
支持 **React 18** 与 Preact。
勾上"包成组件",输出大致是这样的 JSX:
export default function Content() {
return (
<>
<h2>安装</h2>
<p>用你的包管理器安装:</p>
<pre><code className="language-bash">npm install toolora</code></pre>
<p>支持 <strong>React 18</strong> 与 Preact。</p>
</>
);
}
注意三处:bash 语言标记落成了 className="language-bash",高亮器(Prism、Shiki)能直接认;多个块被一个 Fragment <>…</> 裹住,满足单一根节点要求;<strong> 也好好闭合了。改个组件名,塞进 src/docs/,就能用了。
和 dangerouslySetInnerHTML 有什么区别
有人会想:那我把 Markdown 转成 HTML 字符串,再用 dangerouslySetInnerHTML 塞进一个 div 不就行了?能跑,但代价不小。
dangerouslySetInnerHTML 接收的是一团 HTML 字符串,React 对它内部一无所知:你没法对里面某个 <h2> 单独上 props,没法把某段拆成子组件,也享受不到 React 的协调更新。它名字里带 dangerous 不是吓人,字符串里只要混进一段 <script> 或恶意属性,就是 XSS 入口。
转成 JSX 走的是另一条路:输出是真实的 React 元素树,每个节点都受 React 管控,能接 className、能换 props、能被审阅。代码块里的花括号 { } 会被转义成 JSX 安全实体,避免被当成表达式误执行。一句话:dangerouslySetInnerHTML 给你一团黑盒,JSX 给你一棵能动手的树。
全程本地,粘进去就放心
这一步对我很关键。转换是纯 JavaScript,完全在浏览器标签页里跑:Markdown 先解析为 HTML,再用浏览器原生 DOMParser 在一个惰性文档里遍历重建,没有网络请求、不上传、不记录你粘了什么。即使输入里夹了一段行内 <script>,它也只是被当字面文本透传,不会在这里执行。
唯一会"出门"的是分享链接:你的输入会同步进 URL 的 query string,这样"分享带结果"很方便。但这也意味着输入会进浏览器历史和接收方日志,所以含密钥或内部备注的 Markdown,手动复制输出就好,别分享 URL。
这条流水线的上下游
Markdown 转 JSX 本质是两段拼起来的:先 Markdown 转 HTML,再 HTML 转 JSX。如果你手上已经是 HTML 而不是 Markdown,直接用 HTML 转 JSX 工具 跳过前半段更省事。需要边写边看 Markdown 渲染效果,我会先在编辑器里调好再来转换。理解了这条 Markdown → HTML → JSX 的流水线,把任何文档塞进 React 页面都不再是体力活。
Made by Toolora · Updated 2026-06-13