之前在文章中插入示例DEMO是通过异步加载codepen来实现的,但是受制于网络条件,有时候加载缓慢,甚至无法加载。就想着将一些简单的示例直接在Markdown中写为HTML代码输出到前端页面运行,可是这样一来,不仅让Markdown失去了严谨性和优美性,而且如果不小心写了一个可能污染全局的样式或者脚本,那就翻船咯~
但是Markdown最终是要转换成HTML的,因此只要在Markdown的转换过程中做点手脚,再配合上Shadow DOM这个魔法盒子将示例代码运行在一个沙箱中,问题就能得到解决。
我要做什么
一图表达,就是将如图左侧从后台录入的Markdown中带有特殊标记demo的代码块部分转换成图中右侧的可执行示例。Markdown代码块转可执行代码
为什么不能直接渲染
Markdown中是可以直接写HTML的,但考虑到我的使用场景是要在页面中插入一些可运行的代码(不是高亮的那种代码块)作为某个场景的示例DEMO,考虑如下Markdown片段:
background: red;
}
试想一下,这样一段Markdown直接转换为HTML在前端页面运行😱。好吧,我的页面已经变成红色并且所有内容被清空了~
实现思路
在Markdown编译过程中,提取某些有特定标识(如 demo)的代码块,如下图(这里只能用图了,不然就直接运行了):带有demo标识的代码块
默认情况下,它被包裹在pre标签内,将以代码块的形式在页面上展示,而不会直接运行:
带有demo标识的代码块默认展示
因此,在这段代码被Markdown编译进pre标签之前,需要拦截并将其提取。HTML中,template和
最后再使用Web Components创建的自定义标签mo-demo-sandbox引用template的内容来填充影子DOM使其执行。
Shadow DOMWeb components 的一个重要属性是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。其中,Shadow DOM 接口是关键所在,它可以将一个隐藏的、独立的 DOM 附加到一个元素上。-- 引自MDN
我们经常使用的video/audio等标签内部就是Shadow DOM实现的。
自定义Markdown的编译
基于上述实现思路,第一步就是要拦截Markdown对带有特定标识的代码块的编译,我使用的是marked这个库来解析编译Markdown文件的,之前对代码块仅做了代码高亮的处理:
// 原有配置
marked.setOptions({
// ...省略部分配置
langPrefix: 'hljs ',
highlight: (code, lang) => hljs.highlightAuto(code).value
})
highlight配置是无法完全拦截编译的,这里我通过重写rende