玩轉Markdown——資料的分離儲存與元件的原生渲染

玩轉Markdown——資料的分離儲存與元件的原生渲染

玩轉Markdown —— 資料的分離儲存與元件的原生渲染

前言

最近筆者把之前寫的文章(

markdown

)資料,全部同步到資料庫裡,來交給多端去實時渲染。在同步的過程中,卻出現了一些問題。

筆者這裡舉個例子,讓大家有所感受:

<!—— 這是一份markdown檔案 ——>——-author: icebreakermusic: title: ‘喜歡寂寞’ artist: ‘蘇打綠’ src: ‘${{env。CDN_URL}}/music/喜歡寂寞-蘇打綠。m4a’ pic: ‘http://p1。music。126。net/NGBr80seZ96ILO2h8R390A==/18576248952955358。jpg?param=130y130’——-# icebreaker喜歡的音樂

上列文字解析後,會在

瀏覽器

這個環境,使用

icebreaker-love-music

這個元件構建一個音訊播放器,並把在

yaml

裡面宣告的資料,作為

props

傳遞給它,從而達成了在

markdown

中使用

vue

react

web component

元件的效果。

而且透過這個思路,還演化出

MDX

這個格式,大大的增強了

JSX

Markdown

混合書寫時的開發體驗,增強了它的表現能力。

怎麼做到的呢?

我們知道,原生

Markdown

功能很少,不會做任何花哨的事情,這導致它無法滿足大量的場景。於是乎,大量的開發人員充分發揮了

主觀能動性

,定製了許多的

Markdown

編譯器。

以著名的

Typora

為例,它就集成了

flowchart.js

mermaid

這類的圖表庫。我們可以在

md

裡快速的生成一些簡單的圖表,但是遇到複雜的

case

時,可操控性還是遠遠弱於程式碼的。(這種情況,通常會在編輯器外部,先把圖表做好,再把圖片匯出,插入

md

裡)

甚至還出現了

nodeppt

這樣,使用

markdown

來製作

ppt

的包。筆者曾經使用過一段時間,認為使用的場景,還是以部門內部的分享為主。受限於許多難記的語法和

md

自身的表現力,在遇到高自定義化的場景時,製作成本會遠遠超出

powerpoint

markdown 資料的分離儲存

那麼進入正題了,如何對

markdown

內不同的資料進行歸類呢?

我們知道,不進行預處理的話,直接存進資料庫裡,無非就是一堆字串。這堆字串裡藏著的資料,去實時處理,就是對計算機算力的浪費。

許多的

markdown

解析器,也都能夠支援像

yaml

json

toml

csv

等資料格式,此時預先把它們存進資料庫就很有必要了。

怎麼解析呢?

通常的做法就2字,

標記

,在編寫時,把它們用特殊的

flag

標識起來,比較通用的做法有:

---{{code}}---

=>

yaml

---toml{{code}}---

=>

toml

---json{{code}}---

=>

json

這種做法本質上,和程式碼染色類似:

```js(染色語言){{code}}```

於是在標記出來之後,我們就可以非常容易的,對這堆字串,進行

擷取解析

分發給不同的解析引擎處理

了。現有的實現也很多,比如

gray-matter

但是這隻解決了資料分離的問題,還有一個元件渲染的問題沒有解決。

元件的原生渲染

在談這個之前,先看看

md

是如何轉成

html

的:

markded

markdown-it

unified(remark)

為例

它們無非是 把

md

先解析成

tokens/mdast

, 例如:

{ type: ‘root’, children: [ { type: ‘heading’, depth: 2, children: [ {type: ‘text’, value: ‘Hello, ’}, { type: ‘emphasis’, children: [{type: ‘text’, value: ‘World’}] }, {type: ‘text’, value: ‘!’} ] } ]}

然後再交給

html

renderer

去處理的,上述的例子可以很容易的看出它的結果。

那麼非轉化成

html

,而去轉化為原生標籤怎麼做呢?解決方案也有很多。

先說一下我實現的方案:

<icebreaker-love-music :music="music"></icebreaker-love-music>

這一段字串原封不動的存入資料庫中,

然後在其他平臺的場景,都去編寫或者移植一個

Markdown

解析器,接著呢

# 如虛擬碼所示onParse: mdast if: match(node。name , ‘icebreaker-love-music’) then: replace and return (node。attrs)

這種做法本質就是

條件渲染

,相當於一個

if

分支。

這個解決方案需要在不同的平臺上,把

icebreaker-love-music

這個元件都實現一遍,並作為外掛掛載在

Markdown

解析器中。

它的缺點也是很明顯的:

即使各自平臺的生態下,已經存在了優秀的解決方案,但無法保證各自的實現以及外掛的效果。

工作量大,原生需要不同語言,實現相同的元件效果。

死板,當發現獲取的資料中有不明元件,就需要

fallback

處理,這種會造成和後臺那些管理系統的

富文字/Markdown編輯器

,產生高度的耦合,甚至會影響到版本的釋出。

另一種的暢想

另外一種則是我的暢想了,我們能否把元件本身,進行編譯,變成一種

IL

(Intermediate Language)的存在,交給各個端,進行原生渲染呢?

比如我們知道,

web component

瀏覽器端原生支援

vue

元件可以被

@vue/web-component-wrapper

轉化為

web-component

react

則有

react-web-component

那麼

web-component

有可能,能依託一個像

QuickJS

這樣的

Javascript Engine

,在原生環境進行實時的編譯渲染嗎?

以上這些就是筆者的一些愚見,如有想法,歡迎大家討論和指點。

附錄(ast的生成與轉化)

syntax-tree

mdast-util-from-markdown

mdast-util-to-hast