---
详细介绍如何在 React 项目中使用 React-Markdown 库来解析和渲染 Markdown 内容,包含简洁的代码示例和统一模板使用方法
2025-09-03
作者:naiko

本文档将用简单易懂的方式介绍如何在 React 项目中使用 React-Markdown 库,以及如何创建统一的 Markdown 渲染模板,让所有 Markdown 文件都能保持一致的样式和行为。
React-Markdown 是一个 React 库,它能把 Markdown 文本转换成 React 组件,而不是直接转换成 HTML。这样做更安全,也更容易定制样式和行为。
dangerouslySetInnerHTML,避免 XSS 攻击风险首先,在你的项目中安装 React-Markdown:
# 使用 npm
npm install react-markdown
# 使用 pnpm
pnpm add react-markdown
下面是最简单的使用方式,让你快速上手:
import React from 'react';
import ReactMarkdown from 'react-markdown';
const SimpleMarkdown = () => {
// 你的 Markdown 内容
const markdownText = `# 欢迎使用 React-Markdown
这是**粗体文本**,这是*斜体文本*。
## 简单列表
- 项目一
- 项目二
- 项目三`;
return (
<div className="markdown-wrapper">
<ReactMarkdown>{markdownText}</ReactMarkdown>
</div>
);
};
export default SimpleMarkdown;
解释:
实际项目中,我们通常从文件或 API 获取 Markdown:
import React, { useState, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
const ExternalMarkdown = () => {
const [content, setContent] = useState('');
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// 模拟从 API 或文件加载 Markdown
const loadMarkdown = async () => {
try {
setIsLoading(true);
// 真实项目中,这里会是:const response = await fetch('/api/article');
// 然后:const text = await response.text();
// 模拟加载延迟
await new Promise(resolve => setTimeout(resolve, 300));
// 模拟获取到的 Markdown 内容
const markdownFromApi = `# 从外部加载的文章
这是一篇从**服务器**获取的文章。\n\n> 这是一段引用文字\n\n[点击查看更多](https://example.com)`;
setContent(markdownFromApi);
} catch (error) {
console.error('加载 Markdown 失败:', error);
} finally {
setIsLoading(false);
}
};
loadMarkdown();
}, []);
if (isLoading) {
return <div>加载中...</div>;
}
return (
<div className="markdown-wrapper">
<ReactMarkdown>{content}</ReactMarkdown>
</div>
);
};
export default ExternalMarkdown;
解释:
useState 保存 Markdown 内容和加载状态useEffect 在组件加载时获取数据为了让所有 Markdown 文件都使用同一套样式和配置,我们可以创建一个可复用的模板组件。
在 src/ReactComponents 目录下创建一个 MarkdownRenderer.jsx 文件:
import React from 'react';
import ReactMarkdown from 'react-markdown';
import './MarkdownStyle/style.css'; // 用于统一的样式
// 基础的 Markdown 渲染器组件
const MarkdownRenderer = ({ children }) => {
return (
<div className="unified-markdown-container">
<ReactMarkdown>{children}</ReactMarkdown>
</div>
);
};
export default MarkdownRenderer;
在 src/ReactComponents/MarkdownStyle 目录下创建 style.css 文件:
/* Markdown 统一样式 */
.unified-markdown-container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.unified-markdown-container h1 {
color: #2c3e50;
font-size: 2rem;
margin-bottom: 1rem;
border-bottom: 2px solid #eee;
padding-bottom: 0.5rem;
}
.unified-markdown-container h2 {
color: #34495e;
font-size: 1.5rem;
margin-top: 2rem;
margin-bottom: 1rem;
}
.unified-markdown-container p {
margin-bottom: 1rem;
}
.unified-markdown-container strong {
font-weight: 600;
color: #2c3e50;
}
.unified-markdown-container em {
font-style: italic;
}
.unified-markdown-container ul,
.unified-markdown-container ol {
margin-bottom: 1rem;
padding-left: 1.5rem;
}
.unified-markdown-container li {
margin-bottom: 0.5rem;
}
.unified-markdown-container a {
color: #3498db;
text-decoration: none;
}
.unified-markdown-container a:hover {
text-decoration: underline;
}
.unified-markdown-container blockquote {
border-left: 4px solid #3498db;
padding-left: 1rem;
margin-left: 0;
color: #7f8c8d;
font-style: italic;
}
.unified-markdown-container pre {
background-color: #f8f9fa;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
margin-bottom: 1rem;
}
.unified-markdown-container code {
font-family: 'Consolas', 'Monaco', monospace;
background-color: #f8f9fa;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
}
现在,你可以在项目的任何地方引用这个统一的 Markdown 模板:
import React from 'react';
import MarkdownRenderer from '../ReactComponents/MarkdownRenderer';
const MyBlogPage = () => {
// 你的 Markdown 内容
const blogContent = `# 我的博客文章
这是使用**统一模板**渲染的博客文章。
## 文章内容
- 这是第一点
- 这是第二点
> 这是一段引用文字`;
return (
<div className="blog-page">
<h1>博客页面</h1>
<MarkdownRenderer>{blogContent}</MarkdownRenderer>
</div>
);
};
export default MyBlogPage;
为什么要这样做?
要让项目中的所有 Markdown 文件都使用同一套模板,你可以这样做:
如果你的项目使用 Astro,你可以修改 src/layouts/MarkdownPostLayout.astro 文件:
---
import MarkdownRenderer from '../ReactComponents/MarkdownRenderer';
const { content } = Astro.props;
---
<article class="post">
<header class="post-header">
<h1>{content.title}</h1>
<p class="post-date">{content.pubDate}</p>
</header>
<!-- 使用我们的统一 Markdown 渲染器 -->
<div class="post-content">
<MarkdownRenderer>
{content.body}
</MarkdownRenderer>
</div>
</article>
对于从 API 获取的 Markdown 内容,你可以创建一个包装组件:
import React, { useEffect, useState } from 'react';
import MarkdownRenderer from './MarkdownRenderer';
// 通用的 API Markdown 加载组件
const ApiMarkdownLoader = ({ url }) => {
const [markdown, setMarkdown] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchMarkdown = async () => {
try {
setIsLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error('请求失败');
}
const text = await response.text();
setMarkdown(text);
} catch (err) {
setError(err);
} finally {
setIsLoading(false);
}
};
if (url) {
fetchMarkdown();
}
}, [url]);
if (isLoading) {
return <div className="loading">加载中...</div>;
}
if (error) {
return <div className="error">加载失败: {error.message}</div>;
}
return (
<div className="api-markdown-content">
<MarkdownRenderer>{markdown}</MarkdownRenderer>
</div>
);
};
export default ApiMarkdownLoader;
然后在需要加载 Markdown 的地方使用:
import React from 'react';
import ApiMarkdownLoader from './ApiMarkdownLoader';
const HelpPage = () => {
return (
<div className="help-page">
<h1>帮助中心</h1>
<ApiMarkdownLoader url="/api/help-content" />
</div>
);
};
export default HelpPage;
如果你想为代码块添加语法高亮,可以安装 react-syntax-highlighter:
npm install react-syntax-highlighter
然后更新 MarkdownRenderer.jsx:
import React from 'react';
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { tomorrow } from 'react-syntax-highlighter/dist/esm/styles/prism';
import './MarkdownStyle/style.css';
const MarkdownRenderer = ({ children }) => {
// 自定义代码块渲染
const components = {
code: ({ inline, className, children }) => {
// 检查是否是代码块(非内联代码)
const match = /language-(\w+)/.exec(className || '');
if (!inline && match) {
// 代码块,使用语法高亮
return (
<SyntaxHighlighter
language={match[1]}
style={tomorrow}
className="custom-code-block"
>
{String(children)}
</SyntaxHighlighter>
);
}
// 内联代码,保持默认样式
return <code className={className}>{children}</code>;
}
};
return (
<div className="unified-markdown-container">
<ReactMarkdown components={components}>{children}</ReactMarkdown>
</div>
);
};
export default MarkdownRenderer;
你还可以在 Markdown 中直接使用 React 组件,让内容更丰富:
import React from 'react';
import MarkdownRenderer from './MarkdownRenderer';
// 定义一个简单的提示框组件
const NoteBox = ({ children }) => {
return (
<div style={{
backgroundColor: '#f8f9fa',
borderLeft: '4px solid #3498db',
padding: '12px',
margin: '16px 0',
borderRadius: '4px'
}}>
{children}
</div>
);
};
const AdvancedMarkdownPage = () => {
// 在 Markdown 中使用 React 组件
const markdownWithComponents = `# 高级 Markdown 示例
这是一个普通段落。
<NoteBox>
这是一个**提示框**,它是用 React 组件实现的!
</NoteBox>
## 下面是代码示例
\`\`\`javascript
function sayHello() {
console.log('Hello from React-Markdown!');
}
\`\`\``;
// 将自定义组件传递给 MarkdownRenderer
const customComponents = {
NoteBox
};
return (
<div className="advanced-markdown-page">
<MarkdownRenderer components={customComponents}>
{markdownWithComponents}
</MarkdownRenderer>
</div>
);
};
export default AdvancedMarkdownPage;
注意:要在 MarkdownRenderer.jsx 中添加 components 属性的支持:
const MarkdownRenderer = ({ children, components = {} }) => {
// ... 其他代码不变 ...
return (
<div className="unified-markdown-container">
<ReactMarkdown
components={{ ...defaultComponents, ...components }}
>
{children}
</ReactMarkdown>
</div>
);
};
通过本文的指南,你应该已经了解:
使用统一的 Markdown 模板可以让你的项目保持一致的风格,同时也方便后续的维护和更新。无论是博客、文档还是帮助中心,React-Markdown 都能帮助你高效地渲染和管理 Markdown 内容。