乐趣区

关于markdown:玩转Markdown-数据的分离存储与组件的原生渲染

玩转 Markdown —— 数据的拆散存储与组件的原生渲染

前言

最近笔者把之前写的文章 (markdown) 数据,全副同步到数据库里,来交给多端去实时渲染。在同步的过程中,却呈现了一些问题。

笔者这里举个例子,让大家有所感触:

<!-- 这是一份 markdown 文件 -->
---
author: icebreaker
music:
  title: '喜爱寂寞'
  artist: '苏打绿'
  src: '${{env.CDN_URL}}/music/ 喜爱寂寞 - 苏打绿.m4a'
  pic: 'http://p1.music.126.net/NGBr80seZ96ILO2h8R390A==/18576248952955358.jpg?param=130y130'
---

# icebreaker 喜爱的音乐

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

上列文本解析后,会在 浏览器 这个环境,应用 icebreaker-love-music 这个组件构建一个音频播放器,并把在 yaml 外面申明的数据,作为 props 传递给它,从而达成了在 markdown 中应用 vue,react,web component 组件的成果。

而且通过这个思路,还演化出 MDX 这个格局,大大的加强了 JSX 与 Markdown 混合书写时的开发体验,加强了它的体现能力。

怎么做到的呢?

咱们晓得,原生 Markdown 性能很少,不会做任何花哨的事件,这导致它无奈满足大量的场景。于是乎,大量的开发人员充分发挥了 主观能动性 ,定制了许多的Markdown 编译器。

以驰名的 Typora 为例,它就集成了 flowchart.jsmermaid 这类的图表库。咱们能够在 md 里疾速的生成一些简略的图表,然而遇到简单的 case 时,可操控性还是远远弱于代码的。(这种状况,通常会在编辑器内部,先把图表做好,再把图片导出,插入 md 里)

甚至还呈现了 nodeppt 这样,应用 markdown 来制作 ppt 的包。笔者已经应用过一段时间,认为应用的场景,还是以部门外部的分享为主。受限于许多难记的语法和 md 本身的表现力,在遇到高自定义化的场景时,制作老本会远远超出powerpoint

markdown 数据的拆散存储

那么进入正题了,如何对 markdown 内不同的数据进行归类呢?

咱们晓得,不进行预处理的话,间接存进数据库里,无非就是一堆字符串。这堆字符串里藏着的数据,去实时处理,就是对计算机算力的节约。

许多的 markdown 解析器,也都可能反对像 yamljsontomlcsv 等数据格式,此时事后把它们存进数据库就很有必要了。

怎么解析呢? 通常的做法就 2 字,标记 ,在编写时,把它们用非凡的flag 标识起来,比拟通用的做法有:

—\n{{code}}\n— => yaml

—toml\n{{code}}\n— => toml

—json\n{{code}}\n— => json

这种做法实质上,和代码染色相似:

\`\`\`js(染色语言)\n{{code}}\n\`\`\`

于是在标记进去之后,咱们就能够非常容易的,对这堆字符串,进行 截取解析  再  分发给不同的解析引擎解决 了。现有的实现也很多,比方 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 <native component code>(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

退出移动版