开源 | 我将用最优雅的方式在 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 和我们交流.

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

相关推荐
OEC小胖胖4 小时前
去中心化身份:2025年Web3身份验证系统开发实践
前端·web3·去中心化·区块链
vvilkim5 小时前
Electron 进程间通信(IPC)深度优化指南
前端·javascript·electron
字节逆旅7 小时前
如何将markdown生成pdf
markdown
ai小鬼头7 小时前
百度秒搭发布:无代码编程如何让普通人轻松打造AI应用?
前端·后端·github
漂流瓶jz7 小时前
清除浮动/避开margin折叠:前端CSS中BFC的特点与限制
前端·css·面试
前端 贾公子7 小时前
在移动端使用 Tailwind CSS (uniapp)
前端·uni-app
散步去海边7 小时前
Cursor 进阶使用教程
前端·ai编程·cursor
清幽竹客7 小时前
vue-30(理解 Nuxt.js 目录结构)
前端·javascript·vue.js
知性的小mahua7 小时前
echarts+vue实现中国地图板块渲染+省市区跳转渲染
vue.js
weiweiweb8887 小时前
cesium加载Draco几何压缩数据
前端·javascript·vue.js