开源 | 我将用最优雅的方式在 Vue 中渲染 AI 应用返回的 Markdown 数据

想象一个这样的场景, 你的 AI 应用通过知识库查询,为你返回了几十条 Table 数据。产品希望用户能够对 Table 数据执行分页、筛选等操作。

你很容易想到使用你熟知的 UI 组件库, 来渲染 AI 返回的 Table 数据。效果看起来就像这样:

这个库的地址在 github.com/EralChen/vu... .

不过,你也不需要非常着急的使用它,在这篇文章中我将向你揭露一些技术细节,以便你来应对,像 ECharts 图表渲染流程图大纲地图 等一切业务中可能存在的需求。认真看完,希望你会有所收获.

简单实现存在的问题

众所周知, AI 倾向于使用 Markdown 文本数据流向客户端传递信息。 一个简单的渲染,就像:

html 复制代码
<script setup>
// ... 省略其他配置;
const md = MarkdownIt({
    highlight,
})
const htmlText = computedAsync(async () => {
  return md.render(props.source)
}, '')
</script>

<template>
   <div v-html="htmlText"></div>
</template>

<!-- 使用 -->
<MarkdownRender :source="source"></MarkdownRender>

v-html 是一个全量渲染的过程, 并且他仅仅渲染 html 字符串。

这意味着,你不再享有 Vue DOM 更新时的 diff 算法优化, 同时也失去了使用 Vue 组件的权力。

你所期望的调用方式

以替换 Table 这个需求为例,你可以想象最直观的调用方式,就像:

html 复制代码
<!-- 这是一个简单用例, 实际实现考虑到扩展性, 会略有不同 -->
<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 结构对应

  1. Markdown 文本markdown-it tokens(扁平数组)
  2. markdown-it tokens嵌套树状结构
  3. 树状结构Vue 组件渲染(由渲染器处理)
ts 复制代码
// setup
const renderItems = computed(() => tokensToTree(
  md.parse(props.source, {})
))
// template
<VkRenderer :source="renderItems">
   <slot />
</VkRenderer>

一个完整的解析后的数据结构示例 -- h1 标签结构, 如下:

json 复制代码
{
    "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"
        }
    ]
}

而我们唯一要做的, 就是将解析后的结果交给用户, 让用户自定义渲染采用的组件.

我想这就是全部! 是我们最终的目标!

策略渲染

如果你认真阅读, 解析章节, 那么你将发现

ts 复制代码
const renderItems = computed(() => tokensToTree( md.parse(props.source, {}) )) 
// template 
<VkRenderer :source="renderItems"> <slot /> </VkRenderer>

template 中的 VkRenderer 接收的source 正是解析后的树状结构.

再回看解析后的 json 数据, 解析的过程中, 每个 item 都被添加了 templateType 字段.

如果有一个组件能够匹配 templateType 字段, 来渲染内容. 那么我们将轻松

  • 替换默认渲染:为任何 Markdown 元素自定义渲染逻辑
  • 增强交互性:将静态内容转换为交互式组件
  • 集成第三方库:集成语法高亮、图表等功能
  • 保持响应式:利用 Vue 的响应式系统实现动态更新

而你只需要将策略写在组件的默认插槽内, 使用起来就像:

html 复制代码
<script lang="ts" setup>
import { VkMarkdown, VkRendererTemplate } from '@vunk/markdown'
import { computed, ref } from 'vue'

const source = `# Hello, Markdown!
This is a simple example of using **Markdown** in a Vue component.
`
</script>

<template>
  <VkMarkdown
    :source="source"
  >
      
    <VkRendererTemplate type="text">
        <template #default={ raw }>
            {{ raw.content }}
        </template>
    </VkRendererTemplate>
    
    <VkRendererTemplate type="GroupToken">
      <!-- 省略实现 -->
    </VkRendererTemplate>
    
    <!-- 省略 inline 实现 -->
  </VkMarkdown>
</template>

组件库所做的事

很感谢你能看到这里, 如你所见 解析策略渲染 就是 vunk-markdown 的核心.

对于常见的渲染策略, 组件库中做了封装

TemplatesDefault 用于渲染常规 Markdown 内容

TemplateEcharts 用于 Echarts 图表渲染

TemplateMermaid 用于 mermaid 流程图渲染

同时你可以, 在 example 中看到更多自定义渲染

欢迎交流

如果你在实际开发过程中, 也遇到了渲染 AI 返回 Markdown 数据渲染的挑战.

可以通过 Issues 和我们交流.

如果你希望做贡献, 或者提供更多渲染策略的意见, 也随时欢迎~

相关推荐
网络点点滴2 小时前
前端与后端的区别与联系
前端
EnCi Zheng3 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen3 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技3 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人3 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实3 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha3 小时前
三目运算符
linux·服务器·前端
晓晨的博客3 小时前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect4 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript
donecoding4 小时前
别再让 pnpm 跟着 nvm 跑了!独立安装终极指南
前端·node.js·前端工程化