如何在Vue/React组件中合理使用marked.js?
我平时用Markdown写博客内容,靠marked.js渲染到Vue/React搭建的博客里——博客里有自定义的图片画廊、音视频播放器组件,也做过简单的marked.js扩展,但marked.js默认只会输出纯HTML。虽然能用<div v-html="渲染后的HTML">这种方式展示,但我希望能自动把原生<img>这类标签替换成自定义组件,不用手动改内容。下面是我考虑过的三个方案,以及更实用的实现思路:
方案分析
1. 只靠marked.js的词法分析器(lexer)
把解析出的令牌树传给前端,完全自己写渲染逻辑。但marked.js内置20多种令牌,每种参数结构都不一样——比如Heading令牌要根据token.depth渲染h1到h6的组件,等于要重写近一半的marked.js原有逻辑,工作量大到离谱,后续维护也麻烦。
2. 混合方案:内置令牌输出HTML,自定义令牌返回结构化数据
对默认的内置令牌保持HTML输出,自定义的令牌返回结构化数据,再用对应的Vue/React组件渲染,剩下的内容用v-html或者React的dangerouslySetInnerHTML处理。但这个方案有个致命问题:比如表格这类内置令牌生成的HTML里如果嵌套了图片,还是会输出原生<img>,没法替换成自定义组件,覆盖不彻底。
3. 用Vue编译器把生成的HTML转成运行时组件
试过想用Vue编译工具把marked.js生成的HTML解析成可渲染的组件,但这种方式等于在运行时做编译,不仅性能开销大,还会增加打包体积,而且HTML转Vue组件的逻辑很容易出兼容性问题,完全不实用。
更优的实现思路
直接扩展marked.js的Renderer类
这是成本最低也最直接的方案:重写marked.js的Renderer类里对应的方法,比如:
- 重写
image方法,返回自定义图片画廊组件的标签(Vue里就是<CustomImageGallery :src="token.href" :alt="token.text" />) - 重写
video/audio方法,返回自定义播放器组件的标签
之后在Vue/React里,别直接用v-html,而是把marked.js生成的带自定义组件标签的内容,通过框架的编译能力渲染。比如Vue中可以用createVNode或者配合运行时编译把HTML字符串解析为VNode;React里可以用html-react-parser这类库,配置转换规则,把<img>直接替换成自定义组件。
处理嵌套场景的小技巧
如果遇到表格里嵌套图片这种情况,可以用marked.js的walkTokens钩子遍历所有令牌,把嵌套的图片令牌直接替换成自定义组件的令牌,再统一渲染;或者重写表格相关的渲染方法时,先解析单元格内容,把里面的图片标记替换成自定义组件后再拼接成表格结构。
内容的提问来源于stack exchange,提问作者Kasheftin

