解析markdown_markdown-it 原理浅析
前言
最近使用 markdown-it 比較多,也開發了一些插件,在這個過程中對源碼進行了研讀,最終寫了這篇文章。需要了解細節的讀者可以自行閱讀文檔。
此文分為兩個部分:原理剖析和原理應用(編寫插件)。
markdown-it 原理
輸入一串 markdown 代碼,最后得到一串 html 代碼,整體流程如下:
我們以一個簡單的例子來解釋整個流程:?# 我是一個例子? -> ?<h1>我是一個例子</h1>?
首先,它會被解析器拿到,經過各個解析規則處理后得到一個 token 流,接著這個 token 流被渲染器拿到,經過各個渲染規則處理后逐步拼接成一個 html 字符串。
解析器
markdown-it 內置了七個核心規則,在上圖我對解析規則使用了虛線,因為它們是可以被啟用/禁用的。我們這篇文章只來聊聊最核心的兩個規則:block 和 inline。
規范指出:
我們可以將一篇 Markdown 文檔視為一系列塊,塊是一種結構化的元素,如段落,塊引用,列表,標題,規則和代碼塊。一些塊(如塊引號和列表項)可以包含其他塊; 其他(如標題和段落)包含內聯內容,如文本,鏈接,強調文本,圖像,行內代碼等。塊結構的解析優先級始終高于內聯結構。這意味著解析可以分兩步進行:
1.識別 markdown 文檔的塊結構;
2.將段落,標題和其他塊結構中的文本行,作為內聯結構解析。
注意,第一步需要按順序處理行,但第二步可以并行化,因為一個塊元素的內聯解析不會影響任何其他塊的內聯解析。
塊分為兩種類型:容器塊和葉子塊,容器塊可以包含其他塊,但葉子塊不能包含其他塊。
具體解析時,會圍繞著 line 和 character 兩個維度來解析。
對于每一行來說,解釋的結果有以下三種:
對于我們這個例子,會先創建一個 heading 塊,然后將文本內容添加到這個塊上。下一行沒有內容,于是塊關閉。
字符包括非空白字符和空格(?U+0020?),制表符 (?U+0009?),換行符(?U+000A?),行列表(?U+000B?),換頁(?U+000C?)或回車(?U+000D?)這些空白字符。這里我們不做展開。
這期間會接觸到的規則有 block、inline、heading、text。
- 先進入 tokenize 函數,內含十一個 block 規則。
- heading 規則
- 得到 heading_open 、inline、 heading_close 三個 token
- 先進入 parse 函數,內含四個 inline 規則
- text 規則
- 得到 text 的 token
解析完畢,我們得到了 3 + 1 個 token:
token 流
這里我們得到的結果不是一顆 AST 樹,而是一個數組,markdown-it 稱之為 token 流。為什么呢?
官方解釋是:
- Tokens 是一個簡單的數組。(AST 是一個對象)
- 打開的標簽和關閉的標簽可以隔離。
- 將“內聯容器(inline container)”作為一種特殊的 block token 對象。它有嵌套的 tokens,如粗體,斜體,文本等等。
這樣做有什么好處呢?這樣就可以并行處理 block 和 inline 類型的 token 了。
生成 token 流后,它們就被會傳遞給 renderer。
渲染器
它會遍歷所有 token,將每個 token 傳遞給與 token 的 type 屬性同名的規則。markdown-it 內置了九種規則:圍欄、行內代碼、代碼塊、html 塊、行內 html、圖片、硬換行、軟換行、文本。
type 屬性不在內置規則的 token 將會被被傳入 renderToken 中當一個普通 token 處理,這里不作展開。
回到我們的例子中來:
heading_open 會被渲染成 ?<h1>?
inline 中的 text 會被渲染成 ?我是一個例子?
heading_close 會被渲染成 ?</h1>?
markdown-it 插件
一些 markdown-it 插件就利用了上述的原理。
markdown-it-container
這個插件可以讓你支持內容塊:比如 vuepress 的內容塊:
這是如何實現的呢?我們可以根據之前的介紹推測一個內容塊的 token 流:
第一行和第三行有 block 型的 token,一個代表 open,一個代表 close。第二行是 inline 型的 token,其中的內容是 inline 型的。
由于內容塊中是 inline 類型,所以圍欄、行內代碼、代碼塊、html 塊、行內 html、圖片、硬換行、軟換行、文本都是支持的。實際上,我們會逐行掃描,找到匹配 ?::: tip? 這樣的內容塊語法,將它作為一個塊結構開始進行解析,直到有 ?:::? 的行結束。其中的每一行,都將解析為 paragraph_open、inline、paragraph_close。
解析后的 token 流最后分別渲染 ?<div>? 、若干 p 標簽、 ?</div>?。
markdown-it-anchor
這個插件可以對標題進行錨點抽取,以便閱讀文檔時能快速定位位置。
這里也可以推測一下,是不是往原本是 heading_open type 的 token 之前插入了一個 token 呢?這個 token 渲染出來就是錨點。
實際上,的確是插入了 token,但不止一個,因為錨點是可點擊的,所以實際上是一個 a 鏈接,也就是 link_open、inline、link_close 三個 token。而且也不是插入在 heading_open 之前,而是 heading_open 和 heading_close 之間的 inline 子元素里了,因為 ?#? 是和 ?Markdown 語法?平級的。
注意事項:1.因為標題可能是@#$等特殊字符,會造成 url 哈希無效,所以需要對錨點的哈希值轉義。
2.可能會出現重名的標題,所以需要對哈希進行標記
給鏈接添加屬性
官方有一個寫插件的例子:添加 target="_blank" 屬性到所有鏈接。
有兩種方式:
結語
markdown-it 作為一款經典的 js 解析 markdown 的庫,其中思想和設計都可以細細揣摩,回味久久。
總結
以上是生活随笔為你收集整理的解析markdown_markdown-it 原理浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 教师计算机excel培训教案,Excel
- 下一篇: c语言程序设计实践课选题,c语言程序设计