返回

一切为了效率,markdown-it 原理浅析

前端

markdown-it 是一款速度和扩展性都不错的 markdown 解析器,最近使用 markdown-it 比较多,也开发了一些插件,在这个过程中对源码进行了研读,最终写了这篇文章。需要了解细节的读者可以自行阅读文档。

此文分为两个部分:原理剖析和原理应用(编写插件)。

原理剖析

它的处理流程可以简化为:markdown 源码经过解析,得到 Token 流,然后编译得到 html。

markdown-it 自带一个非常小的转义模块,用来转义一些 markdown 的特殊字符,转义的内容包括:

  • HTML 实体。
  • 尖括号(< 和 >)。
  • 反斜杠 ()。
  • 反引号 (`)。
  • 星号 (*)。
  • 下划线 (_)。
  • 波浪线 (~)。
  • 左方括号 ([)。
  • 右方括号 (])。
  • 感叹号 (!)。
  • 分号 (;)。
  • 井号 (#)。

接着,由定义好的编译规则对 Token 流进行编译。编译规则分为:block 规则和 inline 规则。block 规则用于编译块级元素,inline 规则用于编译行内元素。markdown-it 自带了很多编译规则,这些规则覆盖了大部分的 markdown 语法。

编译规则定义了 Token 流到 html 代码的映射关系,也就是说,编译规则告诉解析器如何将 Token 流转换成 html 代码。例如,标题的编译规则定义了如何将标题的 Token 流转换成 html 代码。

编译规则可以分为两类:核心规则和扩展规则。核心规则是 markdown 语法的一部分,扩展规则是 markdown 语法之外的扩展。例如,标题的编译规则是核心规则,表格的编译规则是扩展规则。

最后,将编译后的 html 代码返回给调用者。

原理应用

了解了 markdown-it 的原理之后,就可以编写插件了。markdown-it 的插件机制非常灵活,可以实现很多功能。例如,可以实现语法高亮、代码块折叠、数学公式渲染等等。

编写插件的方法也很简单,只需要定义一个函数,然后将这个函数注册到 markdown-it 实例上即可。例如,以下代码定义了一个语法高亮的插件:

function highlightPlugin(md) {
  md.renderer.rules.code_block = function(tokens, idx, options) {
    return `<pre><code class="hljs">${md.utils.escapeHtml(tokens[idx].content)}</code></pre>`;
  };
}

markdownIt.use(highlightPlugin);

这个插件将代码块的内容用 hljs 库进行语法高亮。

提高效率

markdown-it 的性能已经非常好了,但是如果还有更高的要求,可以考虑以下几个方面:

  • 使用缓存。markdown-it 在解析 markdown 源码时会生成一个 Token 流,这个 Token 流可以缓存起来,下次解析时直接使用缓存的 Token 流,这样可以大大提高解析速度。
  • 并行处理。markdown-it 的解析过程可以并行化,这样可以充分利用多核 CPU 的性能。
  • 使用更快的编译规则。markdown-it 的编译规则是纯 JavaScript 实现的,如果使用更快的语言(例如 C++)实现,可以进一步提高编译速度。

模块划分

markdown-it 的模块划分非常清晰,主要分为以下几个部分:

  • 解析器:负责将 markdown 源码解析成 Token 流。
  • 编译器:负责将 Token 流编译成 html 代码。
  • 渲染器:负责将 html 代码渲染成最终的输出结果。

这样的模块划分使得 markdown-it 非常容易扩展。例如,如果需要实现一个新的语法高亮插件,只需要修改渲染器即可。

文档

markdown-it 的文档非常详细,涵盖了所有 API 和用法。如果在使用 markdown-it 时遇到问题,可以先查阅文档。

总结

markdown-it 是一款速度和扩展性都不错的 markdown 解析器,本文对 markdown-it 的原理进行了剖析,并介绍了如何编写插件和提高效率。希望本文能对大家有所帮助。