goldmark 是 Go 生态中维护最活跃、扩展性强且默认支持 CommonMark 0.30+ 的纯 Go Markdown 解析器;需显式启用 extension.GFM 才支持表格、删除线等 GitHub 风格语法,html.WithUnsafe() 可输出原始 HTML 但有 XSS 风险,实例应全局复用以保障性能。

用 goldmark 渲染 Markdown 是当前最稳妥的选择
Go 生态中,goldmark 是目前维护最活跃、扩展性最强、且默认支持 CommonMark 0.30+ 标准的 Markdown 解析器。它不依赖 CGO,纯 Go 实现,适合嵌入服务或 CLI 工具。相比已停止维护的 blackfriday 或功能较弱的 markdown(github.com/quay/clair/ext/markdown),goldmark 能正确处理表格、脚注、任务列表等现代语法,且扩展接口清晰。
安装命令:go get github.com/yuin/goldmark
基础渲染示例:
import (
"bytes"
"github.com/yuin/goldmark"
)
<p>md := goldmark.New()
var buf bytes.Buffer
if err := md.Convert([]byte("# Hello"), &buf); err != nil {
panic(err)
}
// buf.String() → "<h1>Hello</h1>\n"
启用 GitHub 风格扩展(表格、删除线、任务列表)必须显式配置
默认的 goldmark 实例不开启任何扩展,连 | a | b | 表格都会原样输出。要获得类似 GitHub README 的渲染效果,需手动添加 extension.GFM 扩展。
立即学习“go语言免费学习笔记(深入)”;
-
extension.GFM包含表格、删除线(~~strikethrough~~)、自动链接、任务列表(- [x] done)等 - 它不包含代码高亮——高亮需额外集成
highlighting扩展,并配合 Chroma 或 Pygments 后端(注意:纯 Go 高亮需用chroma库) - 若只想要表格和删除线,但不想引入任务列表解析逻辑,不能只导入部分函数;
GFM是原子扩展,无法拆分
启用方式:
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
)
<p>md := goldmark.New(
goldmark.WithExtensions(extension.GFM),
)
自定义 HTML 渲染时,html.WithUnsafe() 是双刃剑
默认情况下,goldmark 会过滤所有原始 HTML 标签(如 <script>、<iframe>),只保留白名单内标签(<br>、<hr> 等)。如果你需要支持用户输入中嵌入合法 HTML(例如 CMS 后台编辑器),必须显式启用 html.WithUnsafe()。
- 启用后,
<div class="note">xxx</div>会被原样输出,但<script>alert(1)</script>也会透出——XSS 风险完全由你承担 - 不要在面向用户的评论区、富文本提交场景中无条件开启;应在业务层做二次过滤(如用
bluemonday白名单净化) - 它不影响 Markdown 语法解析,只控制 HTML 输出开关
配置示例:
import "github.com/yuin/goldmark/renderer/html" <p>md := goldmark.New( goldmark.WithExtensions(extension.GFM), goldmark.WithRendererOptions( html.WithUnsafe(), // ⚠️ 显式声明才生效 ), )
性能敏感场景下,避免重复创建 goldmark 实例
goldmark.New() 返回的实例是线程安全的,内部缓存了语法树构建器、渲染器等状态。频繁调用 goldmark.New(...)(比如每次 HTTP 请求都新建一个)会导致内存分配激增和 GC 压力升高。
- 推荐全局复用单个实例(
var md = goldmark.New(...)),并发调用Convert()安全 - 如果需要不同配置(例如一个带高亮、一个不带),可预先建好多个实例并复用,而非按需构造
- 注意:扩展(
WithExtensions)和渲染选项(WithRendererOptions)属于初始化配置,不可运行时切换
典型误用:
// ❌ 每次都 new —— 浪费资源
func handler(w http.ResponseWriter, r *http.Request) {
md := goldmark.New(...) // 错!
md.Convert(...)
}
<p>// ✅ 全局一次
var md = goldmark.New(goldmark.WithExtensions(extension.GFM))
func handler(w http.ResponseWriter, r *http.Request) {
md.Convert(...)
}
真正容易被忽略的是扩展组合的隐式依赖:比如启用 extension.GFM 后又想禁用其中某项(如任务列表),goldmark 不提供开关,只能自己 fork 扩展或写自定义解析器——这种定制需求一旦出现,就说明该考虑是否真需要 Markdown 渲染,还是直接上富文本编辑器更合适。


















