跳到主要内容

Markdown 协议与解析渲染(前端 AI 应用)

适合人群:有前端基础,但没有系统学过 Markdown 协议和渲染链路的同学。

目录

Markdown 在 AI 产品中到底是什么角色

很多 AI 应用默认输出 Markdown,不是因为“看起来专业”,而是因为它在“可读性”和“结构化”之间做了平衡。
纯文本虽然简单,但无法表达标题、列表、代码块、表格。HTML 虽然强大,但模型直接输出 HTML 风险更高,且难以约束。

Markdown 的优势在于:

  • 对模型友好:模型更容易稳定输出
  • 对前端友好:可以解析为 AST 再安全渲染
  • 对用户友好:内容结构清晰、可复制、可二次编辑

你可以把 Markdown 理解成“受约束的富文本协议”。

Markdown 解析渲染的完整链路

从工程角度,完整链路不是“字符串 -> 页面”这么简单,而是:

每一步都不可省:

  • Parser:把文本按语法规则切成结构
  • AST:中间表示,方便插件处理
  • Transform:比如代码高亮、数学公式、表格增强
  • Sanitize:过滤恶意标签和危险属性
  • Render:最终变成 React 节点

为什么不能直接 innerHTML

初学者常见做法是把模型输出当 HTML 注入页面,这非常危险。
如果输出中包含恶意脚本片段,可能直接造成 XSS 漏洞,轻则弹窗,重则盗取用户信息。

所以建议始终遵循:

  • 先解析 Markdown
  • 再做白名单级别清洗
  • 最后渲染组件

最小实现:解析 + 安全渲染

下面给一个最小可运行思路(以 marked + DOMPurify 为例):

import { marked } from "marked";
import DOMPurify from "dompurify";

export function renderSafeMarkdown(md: string) {
const rawHtml = marked.parse(md, { breaks: true }) as string;
const safeHtml = DOMPurify.sanitize(rawHtml, {
USE_PROFILES: { html: true },
});
return safeHtml;
}

React 中使用:

export function MarkdownView({ content }: { content: string }) {
const html = renderSafeMarkdown(content);
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

这段代码完成的事情是:

  1. 把 Markdown 转成 HTML
  2. 清理潜在危险标签
  3. 用 React 安全展示

在流式输出场景下的处理策略

AI 聊天里最常见的问题是“流式过程中 Markdown 不完整”,例如代码块只输出了开头的 ```。
如果你每个 token 都强行渲染 Markdown,页面会抖动、格式错乱,甚至卡顿。

推荐策略:

  • 流式阶段:先按纯文本渲染,保证稳定
  • 输出结束:再一次性解析完整 Markdown
  • 或者:每 80-120ms 批量刷新一次,降低重排频率

示例节流:

let buffer = "";
let timer: number | null = null;

export function pushToken(token: string, flush: (text: string) => void) {
buffer += token;
if (timer) return;
timer = window.setTimeout(() => {
flush(buffer);
buffer = "";
timer = null;
}, 100);
}

常见问题与排查清单

  • 代码块渲染异常:检查三引号是否闭合
  • 表格不显示:确认解析器是否启用 GFM
  • XSS 风险:确认是否执行了 sanitize
  • 页面卡顿:减少频繁重渲染,做节流与批量更新