概览:实现 Markdown 支持的核心步骤

无论选择哪种方法,核心逻辑都是一样的:

  1. 输入: 用户在文本框中输入 Markdown 格式的文本。
  2. 解析: 使用一个 JavaScript 库(如 marked)将 Markdown 字符串转换成 HTML 字符串。
  3. 渲染: 将生成的 HTML 字符串插入到网页的指定位置(通常是 <div>)进行显示。
  4. 实时预览 (可选): 在输入框旁边或下方创建一个预览区域,实时显示渲染后的效果。

最简单的方式 - 使用 CDN 和原生 JavaScript

这种方法非常适合快速原型、个人博客或静态网站,无需构建工具。

我们将使用 marked 这个流行、快速且功能强大的 Markdown 解析器。

步骤 1: 创建 HTML 结构

创建一个包含文本输入区和预览区的 index.html 文件。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">Markdown 编辑器</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f4f4f9;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }
        textarea, #preview {
            width: 100%;
            height: 80vh;
            padding: 15px;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-sizing: border-box; /* 确保 padding 不会影响总宽度 */
        }
        textarea {
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            resize: vertical;
        }
        #preview {
            background-color: #fff;
            overflow-y: auto;
        }
        h1 { text-align: center; }
    </style>
</head>
<body>
    <h1>Markdown 实时预览</h1>
    <div class="container">
        <textarea id="editor" placeholder="在这里输入 Markdown..."># 欢迎使用 Markdown 编辑器
这是一个 **粗体** 和 *斜体* 的例子。
## 列表示例
- 项目 1
- 项目 2
  - 子项目 A
  - 子子项目 B
- 项目 3
### 代码块
这是一个行内代码 `console.log('Hello, world!')`。
下面是一个代码块:
```javascript
function greet(name) {
    return `你好, ${name}!`;
}
console.log(greet('Markdown'));

链接和图片

这是一个链接

这是一个引用块。 可以有多行。

<!-- 1. 引入 marked.js 库 -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- 2. 编写 JavaScript 代码 -->
<script>
    // 获取 DOM 元素
    const editor = document.getElementById('editor');
    const preview = document.getElementById('preview');
    // 定义一个更新预览的函数
    function updatePreview() {
        // 使用 marked 将 Markdown 文本转换为 HTML
        const markdownText = editor.value;
        const htmlContent = marked.parse(markdownText);
        // 将生成的 HTML 设置到预览 div 的内容中
        preview.innerHTML = htmlContent;
    }
    // 监听 textarea 的 'input' 事件,当内容改变时更新预览
    editor.addEventListener('input', updatePreview);
    // 页面加载时执行一次,初始化预览内容
    updatePreview();
</script>
```

步骤 2: 运行

直接在浏览器中打开这个 index.html 文件,你就可以看到一个功能完整的实时预览 Markdown 编辑器了。


在现代前端框架中使用(以 React 为例)

如果你在使用 React、Vue 或 Angular 等框架,集成 Markdown 的方式会更模块化。

步骤 1: 安装 marked

在你的项目根目录下运行:

npm install marked
# 或者
yarn add marked

步骤 2: 创建一个可复用的 Markdown 组件

在 React 中,你可以创建一个 MarkdownViewer 组件。

// src/components/MarkdownViewer.js
import React, { useState, useEffect } from 'react';
import { marked } from 'marked';
// 配置 marked,例如启用 GFM (GitHub Flavored Markdown)
marked.setOptions({
  breaks: true, // 允许换行符转换为 <br>
  gfm: true,    // 启用 GitHub 风格的 Markdown
});
function MarkdownViewer({ initialMarkdown }) {
  const [htmlContent, setHtmlContent] = useState('');
  useEffect(() => {
    // 当 initialMarkdown 变化时,重新解析 Markdown
    const html = marked.parse(initialMarkdown);
    setHtmlContent(html);
  }, [initialMarkdown]);
  // 使用 dangerouslySetInnerHTML 来渲染 HTML
  // 注意:这需要你完全信任 Markdown 的来源,以防止 XSS 攻击
  return <div dangerouslySetInnerHTML={{ __html: htmlContent }} />;
}
export default MarkdownViewer;

步骤 3: 在你的应用中使用这个组件

// src/App.js
import React, { useState } from 'react';
import MarkdownViewer from './components/MarkdownViewer';
import './App.css'; // 假设你有一些 CSS
function App() {
  const [markdownText, setMarkdownText] = useState(`# React 中的 Markdown
实时查看渲染效果:
- **列表项 1**
- *列表项 2*
\`\`\`javascript
console.log('Hello from React!');
\`\`\`
`);
  return (
    <div className="app">
      <h1>React Markdown 预览</h1>
      <div className="container">
        <textarea
          value={markdownText}
          onChange={(e) => setMarkdownText(e.target.value)}
          placeholder="输入 Markdown..."
        />
        <div className="preview">
          <MarkdownViewer initialMarkdown={markdownText} />
        </div>
      </div>
    </div>
  );
}
export default App;

步骤 4: 添加样式 (可选)

为了和前面的例子类似,你可以添加一些 CSS:

/* src/App.css */
.app {
  font-family: sans-serif;
  padding: 20px;
}
.container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  max-width: 1200px;
  margin: 0 auto;
}
textarea, .preview {
  width: 100%;
  height: 70vh;
  padding: 15px;
  border: 1px solid #ccc;
  border-radius: 5px;
  box-sizing: border-box;
}
textarea {
  font-family: monospace;
  resize: vertical;
}
.preview {
  background-color: #fff;
  overflow-y: auto;
}

更专业的方案 - 集成代码高亮

Markdown 中的代码块如果能有语法高亮,体验会好很多,我们可以结合 highlight.jsPrism.js 来实现。

这里以 highlight.js 为例,结合方法一。

步骤 1: 修改 HTML

index.html<head> 中引入 highlight.js 的 CSS,在 <body> 底部引入它的 JS。

<head>
    <!-- ... 其他 meta 和 title ... -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
    <!-- ... 你的 style ... -->
</head>
<body>
    <!-- ... 你的 container ... -->
    <!-- 1. 引入 marked.js -->
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <!-- 2. 引入 highlight.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
    <!-- 3. 编写 JavaScript 代码 -->
    <script>
        // ... (获取 editor 和 preview 的代码) ...
        // 配置 marked,使其在渲染代码块时调用 highlight.js
        marked.setOptions({
            highlight: function(code, lang) {
                // 如果指定了语言并且 highlight.js 支持该语言
                if (lang && hljs.getLanguage(lang)) {
                    try {
                        return hljs.highlight(code, { language: lang }).value;
                    } catch (err) {}
                }
                // 否则,使用自动检测
                return hljs.highlightAuto(code).value;
            }
        });
        function updatePreview() {
            const markdownText = editor.value;
            const htmlContent = marked.parse(markdownText);
            preview.innerHTML = htmlContent;
            // **重要步骤**:在渲染完成后,对新生成的代码块应用高亮
            preview.querySelectorAll('pre code').forEach((block) => {
                hljs.highlightElement(block);
            });
        }
        editor.addEventListener('input', updatePreview);
        updatePreview(); // 初始渲染
    </script>
</body>

你的编辑器中的代码块就会拥有漂亮的语法高亮了!


安全性考虑 (非常重要!)

当你将用户输入的 Markdown 渲染成 HTML 时,你打开了 跨站脚本攻击 的风险,如果用户输入了恶意的 <script> 标签,它可能会在你的网站上执行。

如何防范?

  1. 使用 DOMPurify (推荐)DOMPurify 是一个专门用来清理 HTML 的库,它会移除所有潜在的恶意代码,同时保留安全的标签和属性。

    结合方法一使用 DOMPurify

    <!-- 在 marked.js 之后引入 DOMPurify -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.5/purify.min.js"></script>
    <script>
        // ... (获取 editor 和 preview 的代码) ...
        function updatePreview() {
            const markdownText = editor.value;
            // 1. 先用 marked 生成 HTML
            let htmlContent = marked.parse(markdownText);
            // 2. 用 DOMPurify 清理 HTML,防止 XSS
            htmlContent = DOMPurify.sanitize(htmlContent);
            // 3. 再将清理后的 HTML 插入页面
            preview.innerHTML = htmlContent;
        }
        editor.addEventListener('input', updatePreview);
        updatePreview();
    </script>
  2. 限制用户权限:最根本的防御是不要允许不受信任的用户发布带有 <script> 等危险标签的内容,论坛、评论系统等。

总结与建议

场景 推荐方案 优点 缺点
快速原型/静态网站 方法一 (CDN + 原生 JS) 简单、快速、无需构建工具 功能有限,不适合大型应用
现代 Web 应用 方法二 (React/Vue 等框架) 组件化、可复用、与现代工作流无缝集成 需要项目环境和框架知识
专业级编辑器 方法三 + DOMPurify 功能强大(语法高亮)、安全性高 需要引入多个库,配置稍复杂

对于大多数新项目,如果你已经在使用前端框架,方法二 是最佳选择,如果你只是想给一个简单的网页(比如后台管理系统的文章编辑)增加 Markdown 功能,方法一 足够且非常高效。请务必记得在任何情况下都使用 DOMPurify 来保障安全。