Markdown 预览

Heading 1


Heading 2


Heading 3

This is a paragraph with bold and italic text.

  • List item 1

  • List item 2

  • List item 3

  • Numbered item 1

  • Numbered item 2

inline code

const hello = "world";
console.log(hello);

Blockquote text

Link text


| Column 1 | Column 2 |
|----------|----------|
| Cell 1 | Cell 2 |

CommonMark 与 GitHub Flavored Markdown:真正的区别在哪里

Markdown 起源于 2004 年的一个 Perl 脚本和一页写得很松散的语法说明,因此十年间各实现对边界情况的解释各不相同。CommonMark(2014)解决了这个问题:它是一份无歧义的规范,附带 650 多个编号示例,这些示例同时充当一致性测试套件。如今两个渲染器输出不一致时,以 CommonMark 规范为准。 GitHub Flavored Markdown(GFM)是 CommonMark 的正式超集,恰好增加五个扩展:表格、任务列表项、双波浪线删除线、裸 URL 自动链接,以及剥离危险原始 HTML 标签(script、title、iframe、style 等)的 tagfilter。你在 GitHub 上看到的其他功能——[!NOTE] 这类警示引用块、Mermaid 图表、脚注、emoji 短代码——都是 GitHub 产品层面的行为,不属于 GFM 规范,在 npm、GitLab 和多数静态网站生成器上不会渲染。 实践中最容易踩坑的差异:CommonMark 只自动链接尖括号包裹的 URL,而 GFM 会把裸的 www. 和 https:// 文本变成链接。GFM 表格要求表头行下必须有由短横线组成的分隔行,单元格内的竖线字符必须用反斜杠转义。任务列表复选框只有位于列表项开头时才生效。如果文档需要在多个平台上渲染一致,请只用 CommonMark 核心语法,把每个扩展的使用都当作一次关于可移植性的明确决策。
GFM extensions (will NOT render in strict CommonMark):

| Flag | Meaning |
|------|---------|
| -v   | verbose |

- [x] ship parser
- [ ] write docs

~~deprecated~~   www.example.com   <- GFM autolinks bare URLs

CommonMark-portable equivalents:
<https://www.example.com>          <- autolink works everywhere
Cell with \| escaped pipe          <- required inside GFM tables

强调、列表与换行:人人都会绊倒的规则

强调语法看起来简单,实际上是 CommonMark 规范中最复杂的部分——仅 delimiter-run 规则就占了好几页。实用要点:下划线在单词内部不产生强调,所以 snake_case_names 会原样保留;而星号会(a*b*c 会强调 b)。三个星号由外向内嵌套粗体和斜体。强调没有生效时,原因几乎总是分隔符错误一侧的空白——开头星号后紧跟空格就不构成 left-flanking,只能按字面渲染。 列表有两个著名的坑。第一,缩进是相对的:嵌套项必须对齐到父项的第一个内容列,而不是固定的四个空格。父级标记是"1. "时子项从第 3 列开始,"10. "则被推到第 4 列。第二,loose 与 tight 列表:任意两项之间只要有一个空行,所有项都会被段落标签包裹,纵向间距明显变大——这是"我的列表间距怎么忽大忽小"的最常见原因。 有序列表只尊重第一个数字:写 7、8、9 会渲染成从 7 开始的列表,后面的数字被忽略。行首的年份后跟句点("2026. 那一年……")会悄悄变成有序列表——用反斜杠转义句点即可。 换行:单个换行符是软换行,在文件中渲染为一个空格。要强制换行,请在行尾加两个空格或一个反斜杠。GitHub 的评论和 Issue 会把单个换行渲染成硬换行,而 README 文件不会——同样的语法,不同的输出,无尽的困惑。
snake_case_name          -> no emphasis (intraword underscore)
a*b*c                    -> a<em>b</em>c (asterisk works intraword)
* not italic *           -> literal (space after opening delimiter)

7. seven
8. eight                 -> renders as a list starting at 7

2026\. was a good year   -> escaped: stays a paragraph

line one··               (two trailing spaces = hard break)
line two\
line three               (backslash = hard break, survives autoformat)

基于正则的 Markdown 转换器为何会崩坏(真正的解析器如何工作)

串联字符串替换可以转换标题和粗体,但 Markdown 不是正则语言:各种结构可以任意嵌套。行内代码中的星号不能变成强调,围栏代码块中以 # 开头的行不能变成标题,引用块里可以有列表、列表里又可以有引用块。正则流水线按某个固定顺序处理它们,因此总有一种结构会破坏另一种。这对快速预览可以接受,对要发布的内容则很危险。 符合规范的解析器按 CommonMark 规范解析策略附录所述分两阶段工作:先把文档拆成块的树(段落、列表、引用、代码围栏),然后只在允许的块内运行行内解析。这正是代码围栏里可以安全放置"长得像 Markdown"的文本的原因——行内规则根本不会进入其中。 JavaScript 生态的主流选择:markdown-it(快、插件丰富、符合 CommonMark)、micromark + remark(最小的正确内核加 AST 生态,也是 MDX 的引擎)、marked(最老最快,但历史上对边界情况较宽松)。C 语言参考实现 cmark 每秒可解析数兆字节,规范测试正是以它为基准构建的。 无论选哪个,都请用规范的示例套件跑一遍。相当多自称 Markdown 解析器的库会在 650 多个一致性用例中挂掉几十个——而这些失败点恰恰就是你的用户迟早会输入的边界情况。

原始 HTML 与 XSS:渲染前务必消毒

Markdown 在设计上会原样放行原始 HTML——而这种灵活性正是渲染用户提交的 Markdown 时最大的安全隐患。带 onerror 属性的 img 标签会执行 JavaScript;一个完全合法的 Markdown 链接可以指向 javascript: 或 data: URL。这甚至不需要 HTML 块,行内语法就足够了。 请把渲染器输出的 HTML 视为不可信内容。防御有三层:第一,不需要时在解析器里关闭原始 HTML(markdown-it 把 html 设为 false;remark 体系则干脆不加 rehype-raw)。第二,把链接和图片的目标限制在 http、https、mailto 这样的协议白名单内——因为在解析器看来 javascript: 目标也是语法上合法的 Markdown。第三,用真正的消毒器(如 DOMPurify)清洗最终 HTML;手写的标签剔除正则一定会被嵌套或畸形标签绕过。 GitHub 自己的流水线是很好的范本:先渲染 GFM,再应用 tagfilter 扩展(去掉 script、iframe、style 等危险标签),然后经过属性白名单消毒器,内容才会到达页面。注意顺序——要在渲染之后消毒,因为 Markdown 语法本身就能构造出源码里看不见的 HTML。 最后一个陷阱:在自己的浏览器里预览不可信的 Markdown,其中的 HTML 也会被执行。沙箱必须同时存在于预览环境,而不只是发布后的输出。
<!-- all of these are "valid Markdown" from a parser's view -->
<img src=x onerror=alert(document.cookie)>
[click me](javascript:alert(1))
[harmless](data:text/html;base64,PHNjcmlwdD4uLi48L3NjcmlwdD4=)

// safe rendering pipeline
const md = markdownit({ html: false, linkify: true });
const dirty = md.render(userInput);
const clean = DOMPurify.sanitize(dirty, {
  ALLOWED_URI_REGEXP: /^(https?|mailto):/i
});
container.innerHTML = clean;

编写可移植的 Markdown:一份源文件,多个渲染器

同一个 .md 文件可能被 GitHub(GFM)、静态网站生成器、npm 仓库页面、IDE 预览和 pandoc 渲染——引擎各不相同。Hugo 用 Goldmark(符合 CommonMark);Jekyll 默认用 kramdown,它不是 CommonMark,还带有花括号属性列表等自有语法;pandoc 有几十个可选扩展。三短横线之间的 YAML front matter 根本不是 Markdown——不认识它的渲染器会把它以表格或原文形式打印在页面顶部。 划算的可移植性规则:优先使用 ATX 标题(井号)而非 Setext 下划线。永远用带明确语言标签的围栏代码块代替四空格缩进——缩进式代码无法表达语言,而且缩进含义模糊,与列表的交互非常糟糕。在每个块级元素(列表、表格、围栏)前后都留空行,因为各引擎的 lazy continuation 规则不同。不要依赖行尾两个空格构成的硬换行,编辑器和格式化工具保存时经常把它删掉——改用反斜杠。 对于长期维护的文档,值得采用"一句一行"的写法:git diff 可以按句子粒度审阅;软换行会折叠成空格,渲染结果完全一致;还能避免改一个词导致整段重新折行、污染 blame 历史的问题。
---
title: parsed as front matter by Hugo/Jekyll,
       printed as text (or a table!) by others
---

Portable                     Fragile
--------                     -------
# ATX heading                Setext heading underlined with ===
fenced block + language      4-space indented code (no language)
break with backslash \       break with two trailing spaces
blank line before a list     list glued to the previous paragraph
最后更新:

关于此工具

Markdown 预览器实时把 CommonMark / GitHub 风格 Markdown 渲染为 HTML,让你在推送 README、博客或文档前先看效果。Markdown 是技术写作的通用语言,快速预览能省下发布后再修的往返。

使用方法

  1. Type or paste Markdown into the editor on the left.
  2. The preview pane on the right renders updates instantly.
  3. Use standard syntax: # headings, **bold**, `code`, ``` fenced blocks ```, [links](#).
  4. Click Copy HTML to grab the rendered output for use in CMS or email.
  5. Iterate until the layout looks right, then commit the Markdown source to your repo.

常见用例

  • Drafting a README before pushing to GitHub or GitLab.
  • Previewing a blog post for a static site generator like Hugo or Astro.
  • Composing a release note that will be pasted into GitHub Releases.
  • Checking a Pull Request description renders correctly.
  • Producing HTML email content from a Markdown source for a newsletter.
  • Verifying a documentation page before opening a Docs PR.

常见问题

Q. Does this support GitHub-flavored extensions like task lists?

A. Yes — checkboxes, fenced code blocks, autolinks, and tables work. Some platform-specific features (alerts, mermaid diagrams) may render differently on GitHub itself.

Q. Why does my code block lose syntax highlighting?

A. The previewer renders structure, not full syntax themes. GitHub, Gitea, and most static site generators apply their own highlighting on top.

Q. Can I render math (LaTeX)?

A. Not in the basic preview. Some Markdown processors integrate KaTeX or MathJax — check whether your target platform supports it before relying on it.

Q. Is anything sent to a server?

A. No. The Markdown is parsed and rendered entirely in your browser.