結構化文字處理利器 unified 生態介紹

雲音樂大前端團隊 發表於 2022-01-25
作者:imyzf

概述

Content as structured data.

—— unified 官網題詞

unified 是一套文字處理相關的生態體系,結合其生態上的相關外掛,能夠處理 Markdown、HTML、自然語言等。而 unified 庫本身又作為一個統一的執行介面,擔任執行器的角色,呼叫其生態上相關的外掛完成處理任務。

unified 官網 上可以看到,目前 unified 的使用非常廣泛,包括 Prettier、Node.js 官網、Gatsby 都運用了 unified 的能力完成了一些功能。

結構化文字處理利器 unified 生態介紹

圖:unified 官網的使用舉例

常見的使用場景包括:

  • 基於 Markdown 生成 HTML 頁面和站點
  • Markdown/HTML 內容加工處理
  • Markdown 語法檢查、格式化
  • 作為底層庫,封裝特定場景的工具

鑑於目前國內對 unified 體系的介紹文章非常少,本文將對 unified 的相關外掛生態、工作原理作介紹,並對一些使用例子作解析,幫助讀者瞭解 unified 體系的能力、原理和用途。

外掛生態

結構化文字處理利器 unified 生態介紹

圖:unified 生態相關外掛

remark

remark 是 Markdown 相關的外掛集合,提供了 Markdown 的解析、修改、轉換為 HTML 等能力。

目前提供的一些常用外掛:

完整的外掛列表可以參考此處,大約有 150 多個外掛可供選擇。

我們可以在專案中使用這種便捷的方式呼叫 remark:

remark() // 一鍵初始化 Markdown 解析器
  .processSync('# Hello, world!') // 同步處理文字

等價於如下方式:

unified() // 使用 unified 統一的介面
  .use(remarkParse)  // 使用 Markdown 解析器外掛
  .use(remarkStringify) // 使用 Markdown 文字序列化外掛
  .processSync('# Hello, world!')

結構化文字處理利器 unified 生態介紹

圖:remark 使用和轉換示例

另外需要注意的是,GitHub 上目前有一個同名專案 gnab/remark,其官網為 remarkjs.com,雖然也是與 Markdown 相關的工具,但其與 unified 生態下的 remark 沒有任何關係,本文提到的 remark 的官網為 remark.js.org,通過搜尋引擎搜尋相關資料時需要避免混淆。

rehype

與 remark 類似,rehype 是 HTML 相關的外掛集合,提供了 HTML 的格式化、壓縮、文件生成等能力。

相比之下,rehype 的外掛相對較少,只有 40 多個,詳細的外掛列表可以參考外掛列表文件

同時,我們也可以使用rehype-remarkremark-rehype,實現兩種語言的外掛體系之間的互相轉換。例如下面的例子,可以實現將 stdin 輸入的 HTML 內容轉換為 Markdown:

import { unified } from 'unified'
import { stream } from 'unified-stream'
import rehypeParse from 'rehype-parse'
import rehypeRemark from 'rehype-remark'
import remarkStringify from 'remark-stringify'

const processor = unified()
  .use(rehypeParse)     // 解析 HTML
  .use(rehypeRemark)    // 轉換到 remark 體系
  .use(remarkStringify) // 將語法樹轉換為 Markdown 字串

process.stdin.pipe(stream(processor)).pipe(process.stdout)

其他

retextredot 是兩個比較小眾的體系,使用量較少,開發也不如前述兩個體系活躍,其用途如下:

  • retext: 提供自然語言的處理能力,包括拼寫檢查、錯誤修正、可讀性檢查等
  • redot: 提供 graphviz 的解析能力

另外在 Markdown 領域,有兩個命名非 re 開頭的體系,mdxmicromark,分別對應特定的 Markdown 使用場景:

  • mdx: 提供在 Markdown 文件中編寫 JSX 的能力,實現在文件中引入各類元件,編寫可互動的文件
  • micromark: 一個極簡的 Markdown 轉換庫,支援少量擴充套件外掛,適合簡單的 Markdown 轉 HTML 場景,同時 remark 也複用了 micromark 的解析能力

具體的資訊可以檢視專案文件瞭解,這裡不再贅述。

工作原理

unified 的核心機制是基於 AST(abstract syntax trees,抽象語法樹),在執行外掛時 AST 會被傳遞給外掛,可以對其進行各種處理。同時,也可以基於 AST 進行各種語言的轉換,例如將 Markdown 文件解析後,轉換為 HTML 進行處理,之後再轉回 Markdown。

結構化文字處理利器 unified 生態介紹

圖:unified 工作流程

例如我們可以在外掛中遍歷 AST,將所有 heading 節點列印出來:

module.exports = () => tree => {
  visit(tree, 'heading', node => {
    console.log(node)
  })
}

上面例子中的 visit 方法來自 unist-util-visit 工具,提供了遍歷節點的功能。unified 使用了一種稱為 unist 或者 UST 的 AST 標準,使得相同的工具能夠在不同的語言上使用。例如針對 Markdown 和 HTML 語言的 AST,由於他們基於相同的標準,我們可以使用同樣的 visit API 實現同樣的功能:

visit(markdownAST, 'images', transformImages)
visit(htmlAST, 'img', transformImgs)

場景舉例

接下來將列舉一些基於 unified 生態的使用場景,幫助大家進一步瞭解其用途。

Node.js 官網

Node.js 官網主要在語法檢查、文件構建兩個方面使用了 unified:

  • 使用 remark-cli 檢查 Markdown 文件,參考其 package.json 中的指令碼配置
  • 使用 unified 進行文件構建,參考 generate.mjs 中的程式碼

dumi

dumi 是一款為元件開發場景定製的文件工具,其核心功能就是將 Markdown 文件轉換為 HTML 頁面。檢視其原始碼,我們會發現其使用了 unified 作為轉換器,在 remark/index.ts 中引入了 unified,並呼叫了一些列自定義的或者社群提供的外掛進行處理。

由於使用了非常多的自定義外掛,dumi 原始碼可以作為極佳的 unified 外掛開發參考例子。例如參考 link.ts,可以瞭解如何將 Markdown 中的外部連結,通過修改 AST,在生成的頁面中增加一個連結小圖示,提示使用者這是一個指向外部站點的連結。

文件原始碼:

[雲音樂官網](https://music.163.com/)

轉換為:

<a target="_blank" rel="noopener noreferrer" href="https://music.163.com/">
  雲音樂官網
  <svg class="__dumi-default-external-link-icon">……</svg>
</a>

react-markdown

react-markdown 作為 remark 體系的一部分,是基於 unified 生態的上層封裝,提供了一個能夠渲染 Markdown 的 React 元件。在 React 框架中,比起直接使用 remark 將 Markdown 轉換為 HTML 再使用 dangerouslySetInnerHTML 渲染,使用 react-markdown 更加安全可靠,使用方式也更加簡單便捷。

結構化文字處理利器 unified 生態介紹

圖:react-markdown 工作原理

上圖展示了 react-markdown 的工作原理,流程如下:

  1. 通過 remark 將 Markdown 轉換為對應的 AST —— mdast
  2. 使用 remark 外掛對 mdast 進行處理
  3. 通過 remark-rehype 將 mdast 轉換為 HTML 的 AST —— hast
  4. 使用 rehype 外掛對 hast 進行處理
  5. 使用 React 元件渲染 hast 為 React 元素

以上整個流程其實是 Markdown 渲染為 HTML 的通用處理流程 ,我們在實現類似的庫時,也可以作為參考。

關於作者

目前 unified 生態總共有 333 個開源專案(截至 2022.01.05),其核心開發者為 Titus Wormer。從 Wormer 的個人網站可以瞭解到,他來自荷蘭,畢業於阿姆斯特丹應用科學大學,並且曾經擔任過該大學的講師。作為一名全職開源貢獻者,總共維護了 535 多個專案,其中有 50% 的時間精力投入到 unified 專案上。能夠憑一己之力,對開源社群作出如此大的貢獻,非常值得敬佩。關於他是如何管理 unified 組織的,可以參考 unified collective 文件作進一步瞭解。

本文釋出自 網易雲音樂大前端團隊,文章未經授權禁止任何形式的轉載。我們常年招收前端、iOS、Android,如果你準備換工作,又恰好喜歡雲音樂,那就加入我們 grp.music-fe(at)corp.netease.com!