开源 | 我将用最优雅的方式在 Vue 中渲染 AI 应用返回的 Markdown 数据
序想象一个这样的场景, 你的 AI 应用通过知识库查询,为你返回了几十条 Table 数据。产品希望用户能够对 Table 数据执行分页、筛选等操作。
你很容易想到使用你熟知的 UI 组件库, 来渲染 AI 返回的 Table 数据。效果看起来就像这样:
这个库的地址在
github.com/EralChen/vunk-markdown .
不过,你也不需要非常着急的使用它,在这篇文章中我将向你揭露一些技术细节,以便你来应对,像「ECharts 图表渲染」、「流程图」、「大纲」、「地图」等一切业务中可能存在的需求。认真看完,希望你会有所收获.
简单实现存在的问题众所周知, AI 倾向于使用 Markdown 文本数据流向客户端传递信息。 一个简单的渲染,就像:
scriptsetup
// ... 省略其他配置;
constmd = MarkdownIt({
highlight,
})
consthtmlText = computedAsync(async() = {
returnmd.render(props.source)
},'')
/script
template
divv-html="htmlText"/div
/template
!-- 使用 --
MarkdownRender:source="source"/MarkdownRender
v-html是一个全量渲染的过程, 并且他仅仅渲染html字符串。
这意味着,你不再享有 Vue DOM 更新时的 diff 算法优化, 同时也失去了使用 Vue 组件的权力。
你所期望的调用方式以替换 Table 这个需求为例,你可以想象最直观的调用方式,就像:
!-- 这是一个简单用例, 实际实现考虑到扩展性, 会略有不同 --
MarkdownRender:source="source"
template#table="{ data, columns }"
MyTables:data="data":columns="columns"/
/template
/MarkdownRender
如果将数据收集到对应的插槽的参数中,那么我们就能轻松使用任意组件库,来替换原有标签.
解析无论你使用何种 Markdown 工具 (markdown-it、marked、remark), 要想在渲染之前提取数据, 必然绕不开对 markdown 原文本的解析.
这个过程需要封装在MarkdownRender中, 不被使用端感知.
这里仅以 markdown-it 为例, 你可以在 markdown-it demo 查看解析结果
要想让 markdown 解析成 Vue 组件关键是构建嵌套树状结构, 与 vDOM 结构对应
「Markdown 文本」→「markdown-it tokens」(扁平数组)「markdown-it tokens」→「嵌套树状结构」「树状结构」→「Vue 组件渲染」(由渲染器处理)// setup
constrenderItems = computed(()=tokensToTree(
md.parse(props.source, {})
))
// template
VkRenderer :source="renderItems"
slot /
/VkRenderer
一个完整的解析后的数据结构示例 -- h1 标签结构, 如下:
{
"templateType":"GroupToken",
"tag":"h1",
"open": {},// Markdown Tokon 省略
"close": {},// Markdown Tokon 省略
"children": [
{
"type":"inline",
"tag":"",
"attrs":null,
"map": [
0,
1
],
"nesting":0,
"level":1,
"children": [
{
"type":"text",
"tag":"",
"attrs":null,
"map":null,
"nesting":0,
"level":0,
"children":null,
"content":"标题一级",
"markup":"",
"info":"",
"meta":null,
"block":false,
"hidden":false,
"templateType":"text"
}
],
"content":"标题一级",
"markup":"",
"info":"",
"meta":null,
"block":true,
"hidden":false,
"templateType":"inline"
}
]
}
而我们唯一要做的, 就是将解析后的结果交给用户, 让用户自定义渲染采用的组件.
我想这就是全部! 是我们最终的目标!
策略渲染如果你认真阅读, 解析章节, 那么你将发现
constrenderItems = computed(()=tokensToTree( md.parse(props.source, {}) ))
// template
VkRenderer :source="renderItems"slot //VkRenderer
template中的VkRenderer接收的source正是解析后的树状结构.
再回看解析后的json数据, 解析的过程中, 每个 item 都被添加了templateType字段.
如果有一个组件能够匹配templateType字段, 来渲染内容. 那么我们将轻松实现
「替换默认渲染」:为任何 Markdown 元素自定义渲染逻辑「增强交互性」:将静态内容转换为交互式组件「集成第三方库」:集成语法高亮、图表等功能「保持响应式」:利用 Vue 的响应式系统实现动态更新而你只需要将策略写在组件的默认插槽内, 使用起来就像:
scriptlang="ts"setup
import{ VkMarkdown, VkRendererTemplate }from'@vunk/markdown'
import{ computed, ref }from'vue'
constsource =`# Hello, Markdown!
This is a simple example of using **Markdown** in a Vue component.
`
/script
template
VkMarkdown
:source="source"
VkRendererTemplatetype="text"
template#default={raw}
{{ raw.content }}
/template
/VkRendererTemplate
VkRendererTemplatetype="GroupToken"
!-- 省略实现 --
/VkRendererTemplate
!-- 省略 inline 实现 --
/VkMarkdown
/template
组件库所做的事很感谢你能看到这里, 如你所见「解析」和「策略渲染」就是 vunk-markdown 的核心.
对于常见的渲染策略, 组件库中做了封装
TemplatesDefault 用于渲染常规 Markdown 内容
TemplateEcharts 用于 Echarts 图表渲染
TemplateMermaid 用于 mermaid 流程图渲染
同时你可以, 在 example 中看到更多自定义渲染
AI编程资讯AI编码专区指南:
https://aicoding.juejin.cn/aicoding
点击“阅读译文“了解详情~
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线