markdown-it vs marked vs micromark vs remark vs showdown
JavaScript Markdown 解析器技术选型指南
markdown-itmarkedmicromarkremarkshowdown类似的npm包:

JavaScript Markdown 解析器技术选型指南

markdown-itmarkedmicromarkremarkshowdown 都是用于将 Markdown 文本转换为 HTML 或其他格式的 JavaScript 库,但它们在架构、扩展性、性能和安全性方面存在显著差异。markdown-it 以高度可插拔的中间件架构著称,适合需要深度定制的场景;marked 强调简单易用,内置 GitHub Flavored Markdown 支持;micromark 严格遵循 CommonMark 规范,专注于正确性和安全性,常作为底层解析引擎;remark 基于统一的抽象语法树(mdast)构建了丰富的生态系统,适合复杂的文档处理流水线;showdown 作为早期解析器,架构较为陈旧,适用于遗留系统维护。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
markdown-it021,421768 kB613 个月前MIT
marked036,786449 kB1111 天前MIT
micromark02,173210 kB101 年前MIT
remark08,87215.7 kB83 年前MIT
showdown014,853801 kB235-MIT

Markdown 解析器深度对比:markdown-it、marked、micromark、remark 与 showdown

在现代前端工程中,将 Markdown 转换为 HTML 是常见需求 —— 从博客系统、文档站点到富文本编辑器。但不同场景对解析器的要求差异巨大:有的需要极致性能,有的看重扩展能力,有的则追求标准兼容性。本文将从真实开发视角,深入剖析 markdown-itmarkedmicromarkremarkshowdown 的技术特性、适用边界与实现细节。

🧩 核心架构与设计哲学

markdown-it 基于可插拔的中间件架构,将解析过程拆分为多个阶段(如块解析、行内解析),每个阶段都可通过插件拦截和修改 AST。这种设计使其成为高度可定制的“解析器工厂”。

marked 采用经典的递归下降解析器,代码结构相对扁平,强调简单性和快速上手。它内置了常用扩展(如表格、任务列表),但不支持深度定制 AST。

micromark 是一个极简主义的解析器,严格遵循 CommonMark 规范。它不直接生成 HTML,而是输出事件流(tokens),需配合 mdast-util-from-markdown 等工具构建 AST。其核心目标是正确性与安全性。

remark 并非单一解析器,而是一个基于统一抽象语法树(mdast)的生态系统。它使用 micromark 作为底层解析器,通过 unified 处理流程(parse → transform → stringify)实现强大的插件能力。

showdown 是最早的 JavaScript Markdown 解析器之一,采用正则表达式驱动的转换策略。虽然历史悠久,但其架构较陈旧,扩展性有限。

⚠️ 注意:截至 2024 年,showdown 仍处于维护状态,但社区活跃度较低,新项目建议优先评估其他选项。

🔌 扩展能力:如何自定义语法?

自定义链接处理

markdown-it 通过 renderer.rules 直接覆盖 HTML 渲染逻辑:

// markdown-it
const md = require('markdown-it')();
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
  const token = tokens[idx];
  const href = token.attrGet('href');
  if (href && href.startsWith('http')) {
    token.attrSet('target', '_blank');
    token.attrSet('rel', 'noopener');
  }
  return self.renderToken(tokens, idx, options);
};

marked 使用 walkTokens 遍历 AST 节点:

// marked
const marked = require('marked');
marked.use({
  walkTokens(token) {
    if (token.type === 'link') {
      if (token.href.startsWith('http')) {
        token.text = `${token.text} (external)`;
      }
    }
  }
});

micromark 需要创建自定义扩展(extension)来修改 tokenizer:

// micromark
const { micromark } = require('micromark');
const { createRequire } = require('module');
const externalLinkExtension = {
  // 实现复杂的 tokenizer 逻辑(此处简化示意)
  flow: {},
  text: {}
};
const html = micromark(markdown, { extensions: [externalLinkExtension] });

remark 通过插件操作 mdast 节点:

// remark
const remark = require('remark');
const html = require('remark-html');

function externalLinks() {
  return (tree) => {
    visit(tree, 'link', (node) => {
      if (node.url.startsWith('http')) {
        node.children.push({ type: 'text', value: ' (external)' });
      }
    });
  };
}

remark().use(externalLinks).use(html).processSync(markdown).toString();

showdown 通过 addExtension 注册转换规则:

// showdown
const showdown = require('showdown');
const converter = new showdown.Converter();
converter.addExtension(() => ({
  type: 'output',
  filter: (text) => text.replace(/<a href="(https?:\/\/.+?)">/g, '<a href="$1" target="_blank">')
}), 'external-links');

💡 关键区别:markdown-itremark 提供结构化 AST 操作,而 marked/showdown 更依赖字符串替换或浅层节点修改。

⚡ 性能与安全性考量

XSS 防护是 Markdown 解析的核心安全问题。各库处理方式如下:

  • markdown-it 默认不转义 HTML,需显式启用 html: false 或使用 xss 插件。
  • marked 默认禁用原始 HTML(sanitize 已废弃,推荐使用 DOMPurify 后处理)。
  • micromark 本身不生成 HTML,安全性取决于后续的 HTML 生成器(如 hast-util-to-html 默认转义)。
  • remark 通过 rehype-sanitize 插件提供深度 XSS 防护。
  • showdown 提供 noHeaderIdghMentions 等选项,但需手动配置 openLinksInNewWindow 等安全策略。

性能方面

  • micromark 因专注解析且无冗余功能,通常最快。
  • remark 因抽象层较多,在简单场景可能稍慢,但复杂转换中优势明显。
  • markedmarkdown-it 在中等复杂度文档中表现均衡。
  • showdown 性能一般,尤其在处理大文档时。

📦 生态系统与集成

  • remark 拥有最丰富的生态:支持语法树转换(remark-gfm)、代码高亮(remark-highlight.js)、数学公式(remark-math)等。
  • markdown-it 插件市场庞大(如 markdown-it-tocmarkdown-it-emoji),适合需要深度定制的编辑器。
  • marked 内置 GFM 支持,适合快速集成到简单应用。
  • micromark 作为底层引擎,常被 remark 或自定义工具链使用。
  • showdown 插件较少,多用于遗留系统迁移。

🛠️ 典型应用场景

场景 1:静态博客生成器

  • 推荐remark + rehype
  • 理由:需要处理代码块、目录生成、图片优化等复杂转换,统一 AST 生态简化流程。
// remark 示例:添加目录
const remark = require('remark');
const toc = require('remark-toc');
const html = require('remark-html');

remark().use(toc).use(html).processSync(markdown);

场景 2:实时预览的 Markdown 编辑器

  • 推荐markdown-it
  • 理由:高频解析要求高性能,插件机制便于集成语法高亮、自动补全。
// markdown-it 示例:实时渲染
const md = require('markdown-it')({
  html: false,
  linkify: true,
  typographer: true
});
editor.on('input', () => {
  preview.innerHTML = md.render(editor.value);
});

场景 3:轻量级评论系统

  • 推荐marked
  • 理由:只需基础 GFM 支持,快速集成且默认安全策略合理。
// marked 示例:安全渲染
const marked = require('marked');
marked.setOptions({
  gfm: true,
  breaks: true
});
comment.innerHTML = DOMPurify.sanitize(marked.parse(userInput));

场景 4:高安全性要求的 CMS

  • 推荐micromark + hast-util-to-html + rehype-sanitize
  • 理由:分离解析与渲染阶段,确保每一步都可控。
// micromark + rehype 示例
const { fromMarkdown } = require('mdast-util-from-markdown');
const { toHtml } = require('hast-util-to-html');
const sanitize = require('rehype-sanitize');

const tree = fromMarkdown(markdown);
const safeTree = sanitize()(tree);
const html = toHtml(safeTree);

📊 总结:如何选择?

维度markdown-itmarkedmicromarkremarkshowdown
架构可插拔中间件递归下降解析器事件流解析器统一 AST 生态正则转换器
扩展性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ (需组合工具)⭐⭐⭐⭐⭐
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ (含抽象开销)⭐⭐
安全性需手动配置默认较安全依赖下游工具插件化防护需手动加固
学习曲线中等

💡 最终建议

  • 需要深度定制或构建编辑器 → 选 markdown-it
  • 快速集成基础 Markdown 功能 → 选 marked
  • 追求标准兼容性与极致性能 → 选 micromark
  • 构建复杂文档处理流水线 → 选 remark
  • 维护遗留系统 → 评估 showdown,新项目避免使用

如何选择: markdown-it vs marked vs micromark vs remark vs showdown

  • markdown-it:

    选择 markdown-it 如果你需要构建高度可定制的 Markdown 解析流程,例如开发富文本编辑器或需要精细控制 AST 转换。其插件生态丰富,支持从语法扩展到渲染优化的全链路定制,但需自行处理 XSS 防护等安全问题。

  • marked:

    选择 marked 如果你追求快速集成和简单 API,且只需要基础的 GitHub Flavored Markdown 功能(如表格、任务列表)。它默认禁用原始 HTML 提升安全性,适合博客评论、简单文档渲染等轻量级场景。

  • micromark:

    选择 micromark 如果你优先考虑 CommonMark 规范兼容性、解析正确性和极致性能,且愿意组合其他工具(如 mdast-util-from-markdown)构建完整处理链。它本身不生成 HTML,适合需要分离解析与渲染阶段的高安全性应用。

  • remark:

    选择 remark 如果你需要处理复杂的文档转换流水线,例如静态站点生成、自动化文档处理或需要统一操作多种内容格式(Markdown、HTML 等)。其基于统一 AST 的生态提供了强大的插件能力,但学习曲线较陡峭。

  • showdown:

    选择 showdown 仅当你在维护使用该库的遗留系统。其架构基于正则表达式,扩展性和性能有限,新项目应优先评估 markedmarkdown-it 等现代替代方案。

markdown-it的README

markdown-it

CI NPM version Coverage Status Gitter

Markdown parser done right. Fast and easy to extend.

Live demo

  • Follows the CommonMark spec + adds syntax extensions & sugar (URL autolinking, typographer).
  • Configurable syntax! You can add new rules and even replace existing ones.
  • High speed.
  • Safe by default.
  • Community-written plugins and other packages on npm.

Table of content

Install

node.js:

npm install markdown-it

browser (CDN):

Usage examples

See also:

Simple

// node.js
// can use `require('markdown-it')` for CJS
import markdownit from 'markdown-it'
const md = markdownit()
const result = md.render('# markdown-it rulezz!');

// browser with UMD build, added to "window" on script load
// Note, there is no dash in "markdownit".
const md = window.markdownit();
const result = md.render('# markdown-it rulezz!');

Single line rendering, without paragraph wrap:

import markdownit from 'markdown-it'
const md = markdownit()
const result = md.renderInline('__markdown-it__ rulezz!');

Init with presets and options

(*) presets define combinations of active rules and options. Can be "commonmark", "zero" or "default" (if skipped). See API docs for more details.

import markdownit from 'markdown-it'

// commonmark mode
const md = markdownit('commonmark')

// default mode
const md = markdownit()

// enable everything
const md = markdownit({
  html: true,
  linkify: true,
  typographer: true
})

// full options list (defaults)
const md = markdownit({
  // Enable HTML tags in source
  html:         false,

  // Use '/' to close single tags (<br />).
  // This is only for full CommonMark compatibility.
  xhtmlOut:     false,

  // Convert '\n' in paragraphs into <br>
  breaks:       false,

  // CSS language prefix for fenced blocks. Can be
  // useful for external highlighters.
  langPrefix:   'language-',

  // Autoconvert URL-like text to links
  linkify:      false,

  // Enable some language-neutral replacement + quotes beautification
  // For the full list of replacements, see https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.mjs
  typographer:  false,

  // Double + single quotes replacement pairs, when typographer enabled,
  // and smartquotes on. Could be either a String or an Array.
  //
  // For example, you can use '«»„“' for Russian, '„“‚‘' for German,
  // and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
  quotes: '“”‘’',

  // Highlighter function. Should return escaped HTML,
  // or '' if the source string is not changed and should be escaped externally.
  // If result starts with <pre... internal wrapper is skipped.
  highlight: function (/*str, lang*/) { return ''; }
});

Plugins load

import markdownit from 'markdown-it'

const md = markdownit
  .use(plugin1)
  .use(plugin2, opts, ...)
  .use(plugin3);

Syntax highlighting

Apply syntax highlighting to fenced code blocks with the highlight option:

import markdownit from 'markdown-it'
import hljs from 'highlight.js' // https://highlightjs.org

// Actual default values
const md = markdownit({
  highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return hljs.highlight(str, { language: lang }).value;
      } catch (__) {}
    }

    return ''; // use external default escaping
  }
});

Or with full wrapper override (if you need assign class to <pre> or <code>):

import markdownit from 'markdown-it'
import hljs from 'highlight.js' // https://highlightjs.org

// Actual default values
const md = markdownit({
  highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return '<pre><code class="hljs">' +
               hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
               '</code></pre>';
      } catch (__) {}
    }

    return '<pre><code class="hljs">' + md.utils.escapeHtml(str) + '</code></pre>';
  }
});

Linkify

linkify: true uses linkify-it. To configure linkify-it, access the linkify instance through md.linkify:

md.linkify.set({ fuzzyEmail: false });  // disables converting email to link

API

API documentation

If you are going to write plugins, please take a look at Development info.

Syntax extensions

Embedded (enabled by default):

Via plugins:

Manage rules

By default all rules are enabled, but can be restricted by options. On plugin load all its rules are enabled automatically.

import markdownit from 'markdown-it'

// Activate/deactivate rules, with currying
const md = markdownit()
  .disable(['link', 'image'])
  .enable(['link'])
  .enable('image');

// Enable everything
const md = markdownit({
  html: true,
  linkify: true,
  typographer: true,
});

You can find all rules in sources:

Benchmark

Here is the result of readme parse at MB Pro Retina 2013 (2.4 GHz):

npm run benchmark-deps
benchmark/benchmark.mjs readme

Selected samples: (1 of 28)
 > README

Sample: README.md (7774 bytes)
 > commonmark-reference x 1,222 ops/sec ±0.96% (97 runs sampled)
 > current x 743 ops/sec ±0.84% (97 runs sampled)
 > current-commonmark x 1,568 ops/sec ±0.84% (98 runs sampled)
 > marked x 1,587 ops/sec ±4.31% (93 runs sampled)

Note. CommonMark version runs with simplified link normalizers for more "honest" compare. Difference is ≈1.5×.

As you can see, markdown-it doesn't pay with speed for its flexibility. Slowdown of "full" version caused by additional features not available in other implementations.

markdown-it for enterprise

Available as part of the Tidelift Subscription.

The maintainers of markdown-it and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

Authors

markdown-it is the result of the decision of the authors who contributed to 99% of the Remarkable code to move to a project with the same authorship but new leadership (Vitaly and Alex). It's not a fork.

References / Thanks

Big thanks to John MacFarlane for his work on the CommonMark spec and reference implementations. His work saved us a lot of time during this project's development.

Related Links:

Ports