玩轉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
這種做法本質就是
條件渲染
,相當於一個
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