当我问AI,React 如何展示Markdown内容

768 阅读2分钟

React 如何展示Markdown内容

需要安装的依赖

根据您提供的React实现代码,您需要安装以下依赖:

npm install react-markdown katex remark-math remark-breaks rehype-katex remark-gfm react-syntax-highlighter

或使用yarn:

yarn add react-markdown katex remark-math remark-breaks rehype-katex remark-gfm react-syntax-highlighter

各依赖的作用

  • react-markdown: 核心Markdown渲染库
  • katex: 数学公式渲染引擎
  • remark-math: 处理Markdown中的数学公式
  • remark-breaks: 支持Markdown中的换行
  • rehype-katex: 将数学表达式转换为KaTeX渲染
  • remark-gfm: 支持GitHub风格Markdown(表格、任务列表等)
  • react-syntax-highlighter: 代码语法高亮

组件实现

import ReactMarkdown from 'react-markdown'
import 'katex/dist/katex.min.css'
import RemarkMath from 'remark-math'
import RemarkBreaks from 'remark-breaks'
import RehypeKatex from 'rehype-katex'
import RemarkGfm from 'remark-gfm'
import SyntaxHighlighter from 'react-syntax-highlighter'
import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs'

export function Markdown(props: { content: string }) {
  return (
    <div className="markdown-body">
      <ReactMarkdown
        remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
        rehypePlugins={[
          RehypeKatex,
        ]}
        components={{
          code({ node, inline, className, children, ...props }) {
            const match = /language-(\w+)/.exec(className || '')
            return (!inline && match)
              ? (
                <SyntaxHighlighter
                  {...props}
                  children={String(children).replace(/\n$/, '')}
                  style={atelierHeathLight}
                  language={match[1]}
                  showLineNumbers
                  PreTag="div"
                />
              )
              : (
                <code {...props} className={className}>
                  {children}
                </code>
              )
          },
        }}
        linkTarget={'_blank'}
      >
        {props.content}
      </ReactMarkdown>
    </div>
  )
}

如何使用组件

1. 在组件中导入并使用

import React from 'react';
import { Markdown } from './path/to/Markdown';

function App() {
  const markdownContent = `
# 这是一个标题

这是普通文本内容

## 代码示例
\`\`\`javascript
const hello = "world";
console.log(hello);
\`\`\`

## 数学公式
$E = mc^2$

## 表格
| 姓名 | 年龄 |
| ---- | ---- |
| 张三 | 20   |
| 李四 | 25   |
  `;

  return (
    <div className="app">
      <h1>Markdown预览</h1>
      <Markdown content={markdownContent} />
    </div>
  );
}

export default App;

2. 添加CSS样式

您需要确保已加载KaTeX的CSS样式。在您的主入口文件(如index.jsApp.js)中导入:

import 'katex/dist/katex.min.css';

另外,您可能需要添加一些基本的Markdown样式。建议添加类似GitHub的Markdown样式:

npm install github-markdown-css

然后在入口文件中导入:

import 'github-markdown-css/github-markdown.css';

3. 组件功能说明

您提供的Markdown组件具有以下功能:

  • 支持基本Markdown语法(标题、列表、加粗、斜体等)
  • 支持数学公式(通过KaTeX渲染)
  • 支持代码语法高亮(使用atelierHeathLight主题)
  • 支持GitHub风格Markdown(表格、任务列表等)
  • 链接自动在新标签页中打开
  • 支持换行转换

4. 定制组件

如果需要修改代码高亮主题,可以从react-syntax-highlighter/dist/esm/styles/hljs导入其他主题:

import { docco, github, monokai } from 'react-syntax-highlighter/dist/esm/styles/hljs';

然后在SyntaxHighlighter组件中使用:

<SyntaxHighlighter
  style={github} // 更改主题
  language={match[1]}
  showLineNumbers
  PreTag="div"
>
  {String(children).replace(/\n$/, '')}
</SyntaxHighlighter>

完整使用示例

// App.js
import React, { useState } from 'react';
import { Markdown } from './components/Markdown';
import 'katex/dist/katex.min.css';
import './App.css';

function App() {
  const [markdownText, setMarkdownText] = useState(`# React Markdown 演示

这是一个简单的 **Markdown** 渲染示例。

## 代码示例

\`\`\`javascript
function greeting(name) {
  return \`Hello, \${name}!\`;
}

console.log(greeting("React"));
\`\`\`

## 数学公式

行内公式: $E = mc^2$

独立公式:

$$
\\frac{\\partial f}{\\partial x} = \\lim_{h \\to 0} \\frac{f(x+h) - f(x)}{h}
$$

## 表格

| 功能 | 支持 |
|------|------|
| 表格 | ✅ |
| 代码高亮 | ✅ |
| 数学公式 | ✅ |
| 自动换行 | ✅ |

`);

  return (
    <div className="app-container">
      <div className="editor-section">
        <h2>Markdown 编辑器</h2>
        <textarea
          value={markdownText}
          onChange={(e) => setMarkdownText(e.target.value)}
          className="markdown-editor"
        />
      </div>
      <div className="preview-section">
        <h2>预览结果</h2>
        <div className="preview-container">
          <Markdown content={markdownText} />
        </div>
      </div>
    </div>
  );
}

export default App;

/* App.css */
.app-container {
  display: flex;
  height: 100vh;
  padding: 20px;
  box-sizing: border-box;
}

.editor-section, .preview-section {
  flex: 1;
  padding: 0 15px;
}

.markdown-editor {
  width: 100%;
  height: calc(100vh - 100px);
  padding: 15px;
  font-family: monospace;
  font-size: 14px;
  border: 1px solid #ddd;
  border-radius: 4px;
  resize: none;
}

.preview-container {
  border: 1px solid #eee;
  border-radius: 4px;
  padding: 20px;
  height: calc(100vh - 100px);
  overflow: auto;
  background-color: white;
}

/* 为markdown-body类添加基本样式 */
.markdown-body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  color: #24292e;
  line-height: 1.6;
}

.markdown-body code:not([class*="language-"]) {
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  background-color: rgba(27, 31, 35, 0.05);
  border-radius: 3px;
  padding: 0.2em 0.4em;
}

.markdown-body pre {
  margin: 1em 0;
}

通过这个示例,您可以创建一个完整的Markdown编辑和预览应用程序,用户可以在左侧输入Markdown文本,右侧实时显示渲染后的结果。

性能优化建议

如果您的应用需要处理大量Markdown内容,可以考虑以下优化措施:

  1. 使用React.memo包装Markdown组件
  2. 在textarea输入时使用防抖处理
  3. 对于非常大的文档,考虑使用虚拟滚动

祝您的Markdown实现顺利!