Markdown支持实现指南
引言
本文使用一个AI聊天系统作为示例,讲解了如何在应用中支持markdown渲染,在最后提供了一个可运行的简单代码,如果着急的话可以直接跳转到最后ctrlcv。
项目概述
这是一个基于FastAPI后端和Vue.js前端的AI聊天系统,支持实时Markdown渲染。系统能够将AI助手返回的Markdown格式文本转换为美观的HTML显示效果。
核心技术栈
前端技术
- Vue.js 3: 前端框架
- marked.js: Markdown解析库
- DOMPurify: HTML安全净化库
- highlight.js: 代码语法高亮
后端技术
- FastAPI: Web框架
- LangChain: AI对话管理
- DeepSeek API: AI模型服务
Markdown支持的实现架构
📋 实现步骤概览
- 前端HTML模板 → 引入必要的JavaScript库
- 前端JavaScript → 实现Markdown渲染逻辑
- 前端CSS样式 → 美化Markdown显示效果
- 后端API接口 → 处理和返回Markdown内容
🎯 第一步:前端HTML模板设置
文件位置: templates/index.html
1.1 在HTML头部引入依赖库
<head>
<!-- 其他头部内容... -->
<!-- 🔧 Markdown支持必需的库 -->
<!-- Markdown解析器 -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- 代码高亮支持 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/styles/github.min.css">
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/lib/core.min.js"></script>
<!-- 防XSS攻击安全库 -->
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.5/dist/purify.min.js"></script>
</head>
1.2 修改消息显示结构
<!-- 在消息循环中添加Markdown渲染 -->
<div v-for="msg in messages" :key="msg.id"
:class="['message', msg.role]">
<!-- 🎨 关键:使用v-html渲染Markdown -->
<div class="message-content markdown-body"
v-html="msg.role === 'assistant' ? renderMarkdown(msg.content) : msg.content">
</div>
</div>
🛠️ 第二步:前端JavaScript渲染逻辑
文件位置: templates/index.html
(在 <script>
标签内)
2.1 添加Markdown渲染函数
// 在Vue应用的setup()函数中添加
const renderMarkdown = (text) => {
try {
// 🔄 第一步:使用marked.js解析Markdown语法
const rendered = marked.parse(text);
// 🛡️ 第二步:使用DOMPurify净化HTML,防止XSS攻击
return DOMPurify.sanitize(rendered);
} catch (e) {
console.error('Markdown渲染错误:', e);
return text; // 渲染失败时返回原文本
}
};
2.2 在返回对象中导出函数
// 在setup()函数的return语句中添加
return {
// ...其他现有属性...
renderMarkdown // 导出供模板使用
};
🎨 第三步:CSS样式美化
文件位置: static/css/style.css
或 templates/index.html
的 <style>
标签内
3.1 基础Markdown样式
/* 🎯 Markdown内容容器基础样式 */
.markdown-body {
font-size: 14px;
line-height: 1.6;
word-wrap: break-word;
color: #333;
}
/* 📝 标题样式 */
.markdown-body h1, .markdown-body h2, .markdown-body h3 {
margin-top: 1em;
margin-bottom: 0.5em;
font-weight: 600;
}
/* 📋 段落样式 */
.markdown-body p {
margin: 0.8em 0;
}
3.2 代码块专用样式
/* 💻 代码块样式 */
.markdown-body pre {
background: #f6f8fa;
border-radius: 6px;
padding: 16px;
overflow: auto;
font-family: 'Consolas', 'Monaco', monospace;
border: 1px solid #e1e4e8;
}
/* 🔤 行内代码样式 */
.markdown-body code {
background-color: rgba(175,184,193,0.2);
border-radius: 4px;
font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
font-size: 0.92em;
padding: 0.2em 0.4em;
}
/* 🎨 代码块内的代码不需要背景 */
.markdown-body pre code {
background: transparent;
padding: 0;
}
3.3 表格和引用样式
/* 📊 表格样式 */
.markdown-body table {
border-collapse: collapse;
margin: 1em 0;
width: 100%;
border: 1px solid #d0d7de;
}
.markdown-body table th,
.markdown-body table td {
border: 1px solid #d0d7de;
padding: 6px 13px;
text-align: left;
}
.markdown-body table th {
background-color: #f6f8fa;
font-weight: 600;
}
/* 📖 引用块样式 */
.markdown-body blockquote {
color: #57606a;
border-left: 4px solid #d0d7de;
margin: 0.5em 0;
padding: 0 1em;
background-color: #f8f9fa;
}
/* 📝 列表样式 */
.markdown-body ul, .markdown-body ol {
padding-left: 2em;
margin: 0.5em 0;
}
.markdown-body li {
margin: 0.2em 0;
}
🚀 第四步:后端API支持
文件位置: fast_app.py
4.1 确保API返回原始Markdown内容
@app.post("/chat")
async def single_chat(request: Request):
"""单轮对话API - 返回原始Markdown文本"""
data = await request.json()
user_input = data.get('message', '')
# 获取会话和聊天系统
session_id = get_session_id(request)
chat_system = get_chat_system(session_id)
# 🔄 获取AI响应(可能包含Markdown格式)
response = chat_system.single_turn_chat(user_input)
# 📤 直接返回原始响应,让前端处理Markdown渲染
return JSONResponse(content={"response": response, "type": "single"})
4.2 多轮对话支持
@app.post("/multi_chat")
async def multi_chat(request: Request):
"""多轮对话API - 支持上下文的Markdown响应"""
data = await request.json()
user_input = data.get('message', '')
session_id = get_session_id(request)
chat_system = get_chat_system(session_id)
# 🔄 多轮对话,保持上下文
response = chat_system.multi_turn_chat(user_input)
return JSONResponse(content={"response": response, "type": "multi"})
📋 实现检查清单
完成以下步骤即可启用Markdown支持:
- 步骤1: 在HTML中引入必要的JavaScript库
- 步骤2: 添加
renderMarkdown
函数到Vue应用 - 步骤3: 修改消息显示模板使用
v-html
- 步骤4: 添加CSS样式美化Markdown显示
- 步骤5: 确保后端API返回原始Markdown内容
- 步骤6: 测试各种Markdown语法(标题、代码、表格等)
🔧 调试提示
- 使用浏览器开发者工具检查JavaScript错误
- 确认所有CDN库都正确加载
- 检查CSS样式是否正确应用
- 验证API响应格式是否正确
实现要点分析
1. 安全性考虑
- XSS防护: 使用DOMPurify净化HTML内容
- 内容过滤: 移除
<think>
标签等不需要的内容 - 错误处理: 渲染失败时降级到纯文本显示
2. 性能优化
- CDN加载: 使用CDN加载第三方库
- 按需渲染: 只对AI响应进行Markdown渲染
- 缓存机制: Vue的响应式系统提供自动缓存
3. 用户体验
- 实时渲染: 消息接收后立即渲染
- 语法高亮: 代码块自动高亮显示
- 响应式设计: 适配不同屏幕尺寸
支持的Markdown特性
基础语法
- 标题:
# ## ###
等级标题 - 段落: 自动换行和段落分割
- 强调:
**粗体**
和*斜体*
- 列表: 有序和无序列表
高级特性
- 代码块: 支持语法高亮的代码块
- 行内代码: 行内代码片段
- 表格: 完整的表格支持
- 引用: 块引用格式
- 链接: 自动链接转换
总结
本项目通过前端JavaScript库和后端API的有机结合,实现了完整的Markdown支持系统。核心思路是:
- 后端专注: AI模型生成Markdown格式内容
- 前端渲染: 浏览器端实时解析和美化显示
- 安全第一: 通过DOMPurify确保内容安全
- 体验优先: 提供流畅的实时渲染体验
这种架构既保证了系统的安全性,又提供了良好的用户体验,是现代Web应用中Markdown支持的标准实现方案。
快速入门示例
简单的Markdown渲染演示
以下是一个可以直接运行的HTML文件,演示了基础的Markdown渲染功能:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown渲染演示</title>
<!-- 引入必要的库 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.5/dist/purify.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/styles/github.min.css">
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/lib/core.min.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.input-area {
margin-bottom: 20px;
}
textarea {
width: 100%;
height: 200px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: monospace;
resize: vertical;
}
.render-btn {
background: #2196f3;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.render-btn:hover {
background: #1976d2;
}
.output {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
background: #fafafa;
min-height: 100px;
}
/* Markdown样式 */
.markdown-body {
line-height: 1.6;
}
.markdown-body h1, .markdown-body h2, .markdown-body h3 {
border-bottom: 1px solid #eee;
padding-bottom: 5px;
}
.markdown-body pre {
background: #f6f8fa;
border-radius: 6px;
padding: 16px;
overflow: auto;
}
.markdown-body code {
background-color: rgba(175,184,193,0.2);
border-radius: 4px;
padding: 0.2em 0.4em;
font-family: monospace;
}
.markdown-body blockquote {
border-left: 4px solid #dfe2e5;
padding: 0 1em;
color: #6a737d;
}
.markdown-body table {
border-collapse: collapse;
width: 100%;
}
.markdown-body table th, .markdown-body table td {
border: 1px solid #dfe2e5;
padding: 6px 13px;
}
.markdown-body table th {
background-color: #f6f8fa;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h1>🎯 Markdown渲染演示</h1>
<p>在下面的文本框中输入Markdown内容,点击渲染按钮查看效果:</p>
<div class="input-area">
<textarea
v-model="markdownText"
placeholder="在这里输入Markdown内容...">
</textarea>
<button class="render-btn" @click="renderContent">🚀 渲染Markdown</button>
</div>
<h3>渲染结果:</h3>
<div class="output markdown-body" v-html="renderedHtml"></div>
</div>
</div>
<script>
const { createApp, ref } = Vue;
createApp({
setup() {
// 示例Markdown内容
const markdownText = ref(`# 欢迎使用Markdown渲染器
## 功能特性
这是一个简单的**Markdown渲染演示**,支持以下特性:
- *斜体文字*
- **粗体文字**
- \`行内代码\`
- [链接](https://github.com)
### 代码块示例
\`\`\`javascript
function hello() {
console.log("Hello, Markdown!");
}
\`\`\`
### 表格示例
| 特性 | 支持 | 说明 |
|------|------|------|
| 标题 | ✅ | 1-6级标题 |
| 列表 | ✅ | 有序/无序列表 |
| 代码 | ✅ | 行内和代码块 |
> 这是一个引用块,用于强调重要内容。
### 任务列表
- [x] 基础Markdown语法
- [x] 代码高亮
- [ ] 数学公式(待扩展)
- [ ] 流程图(待扩展)`);
const renderedHtml = ref('');
// Markdown渲染函数
const renderMarkdown = (text) => {
try {
// 使用marked.js解析Markdown
const rendered = marked.parse(text);
// 使用DOMPurify净化HTML,防止XSS攻击
return DOMPurify.sanitize(rendered);
} catch (e) {
console.error('Markdown渲染错误:', e);
return '渲染失败,请检查输入内容';
}
};
// 渲染内容
const renderContent = () => {
renderedHtml.value = renderMarkdown(markdownText.value);
};
// 初始渲染
renderContent();
return {
markdownText,
renderedHtml,
renderContent
};
}
}).mount('#app');
</script>
</body>
</html>
使用方法
- 创建HTML文件:将上述代码保存为
markdown-demo.html
- 直接打开:用浏览器打开该文件即可运行
- 编辑测试:在文本框中修改Markdown内容,点击渲染按钮查看效果
示例特性
- ✅ 即时渲染:支持实时Markdown到HTML转换
- ✅ 安全防护:使用DOMPurify防止XSS攻击
- ✅ 完整语法:支持标题、列表、代码、表格、引用等
- ✅ 响应式设计:适配不同屏幕尺寸
- ✅ 代码高亮:自动为代码块添加语法高亮
扩展练习
尝试在文本框中输入以下内容测试不同功能:
## 简单markdown内容
### 1. 数学表达式(需要额外支持)
$$E = mc^2$$
### 2. 任务列表
- [x] 已完成任务
- [ ] 待完成任务
### 3. 表情符号
:smile: :heart: :thumbsup:
### 4. 脚注
这是正文[^1]。
[^1]: 这是脚注内容。