别再盲目混用!AI 组件库和传统组件库差距原来这么大
最近团队里有个争论:做一个 AI 对话界面,到底该用 Element Plus 还是 TinyRobot?有人说"Element Plus 生态成熟,组件多,什么都能做";有人说"TinyRobot 专门做 AI,肯定更合适"。
两边都有道理,但争了半天也没结论------因为大家压根没搞清楚"AI 组件库"和"传统组件库"的本质差异。
这篇文章不站队,只做一件事:把两种组件库从架构层面到具体功能的差异,掰开揉碎对比清楚。看完之后,你就不会再盲目混用了。
架构差异:AI 原生 vs 表单原生
所有前端组件库都有一个底层设计哲学------它决定了组件的 API 设计、数据模型、渲染机制、状态管理方式。
传统组件库(Element Plus、Ant Design Vue、TinyVue)的设计哲学是"表单原生":
- 核心交互模型:用户操作 → 事件触发 → 状态更新 → UI 渲染
- 数据流向:单向,从用户输入到 UI 展示
- 核心组件:Button、Input、Table、Form、Dialog------解决"数据录入"和"信息展示"
- 状态模型:组件级,每个组件管理自己的内部状态
AI 组件库(TinyRobot)的设计哲学是"AI 原生":
- 核心交互模型:用户输入 → AI 推理 → 流式输出 → 持续更新 UI
- 数据流向:双向流式,用户输入和 AI 输出同时驱动 UI
- 核心组件:Bubble、Sender、Container、Prompts------解决"AI 对话"和"智能交互"
- 状态模型:引擎级,useMessage 管理整个对话的数据流和请求生命周期
这两种设计哲学的差异,导致了几乎每个层面的具体不同。
渲染差异:流式 vs 静态
这是最直观的差异,也是很多人最先感受到的。
传统组件库的渲染机制是"静态渲染":
组件接收一次数据,渲染一次 UI。数据变了,重新渲染。这是 DOM 操作的基本模式,对所有 CRUD 场景都够用。
但 AI 对话场景不一样------AI 的回复是流式的,一段一段"吐出来"。一个 300 字的回复,可能被拆成 50 个 chunk 陆续到达。每个 chunk 到达时,UI 都需要更新。
传统库做流式渲染的方式:
vue
<!-- 传统方案:手动拼接 + 强制更新 -->
<template>
<div class="chat-content" v-html="renderedContent"></div>
</template>
<script setup>
const content = ref('')
const renderedContent = ref('')
// SSE 解析
async function handleSSE() {
const response = await fetch('/api/chat')
const reader = response.body.getReader()
while (true) {
const { done, value } = await reader.read()
if (done) break
content.value += decodeChunk(value)
// 每次更新都要重新渲染 Markdown → 性能问题
renderedContent.value = markdownit.render(content.value)
}
}
</script>
这种方案的问题:
- 每收到一个 chunk 就重新渲染整个 Markdown------性能开销大
- 光标动画需要手写 CSS
- 用户滚动位置在频繁更新时容易跳动
- 代码块渲染(语法高亮)在部分更新时容易错乱
TinyRobot 的流式渲染机制:
vue
<!-- TinyRobot 方案:content 响应式,组件内部优化增量渲染 -->
<template>
<BubbleProvider :fallback-content-renderer="BubbleRenderers.Markdown">
<TrBubble role="assistant" :content="streamingText" />
</BubbleProvider>
</template>
<script setup>
const streamingText = ref('')
// SSE 解析
async function handleSSE() {
// ... 同样的 SSE 解析逻辑
streamingText.value += decodedChunk // 直接赋值,Bubble 内部处理渲染优化
}
</script>
TinyRobot 的 Bubble 组件针对流式场景做了底层优化:
content是响应式的,但组件内部做了增量渲染策略,避免每次 chunk 都重建整个 DOM- 自动光标动画(打字效果的跳动光标)
- Markdown 渲染支持增量更新,不重建整个文档树
- 消息分组策略避免频繁重排 DOM
这些优化,用传统组件库你得自己实现,而且很可能写不好。
数据模型差异:OpenAI 消息格式 vs 表单数据
数据模型是更深层的差异,直接影响你的代码架构。
传统组件库的数据模型是"表单数据":
typescript
// 传统方案:自定义数据结构
interface Message {
id: string
type: 'user' | 'bot'
text: string
timestamp: number
status: 'sending' | 'sent' | 'error'
}
这种数据结构的问题是:
- 不兼容 OpenAI 的 ChatMessage 格式------每次调 API 都得做数据转换
- 无法表达
tool_calls(工具调用)------type只有 'user' 和 'bot' - 无法表达
reasoning_content(思考过程)------没有这个字段 - 无法表达多角色消息------只有两个 type
TinyRobot 的数据模型是"OpenAI 消息格式":
typescript
// TinyRobot:直接使用 OpenAI ChatMessage 格式
interface BubbleMessage {
role?: string // 'user' | 'assistant' | 'tool' | 自定义
content?: string | ChatMessageContentItem[] // 文本或多模态内容
reasoning_content?: string // 思考过程
tool_calls?: ToolCall[] // 工具调用
tool_call_id?: string // 工具调用关联ID
name?: string // 消息名称
id?: string // 消息ID
loading?: boolean // 加载状态
state?: Record<string, unknown> // UI 状态(不影响消息内容)
}
关键差异:
| 字段 | 传统方案 | TinyRobot |
|---|---|---|
role |
只有 user/bot | 支持 user/assistant/tool/自定义 |
content |
纯字符串 | 字符串或数组(多模态:文本+图片+混合) |
reasoning_content |
没有 | 原生支持,thinkingPlugin 自动处理 |
tool_calls |
没有 | 原生支持,toolPlugin 自动处理 |
state |
没有 | UI 状态与消息内容分离 |
最关键的一点:TinyRobot 的数据格式直接兼容 OpenAI API 的返回值 。你从 API 收到一条消息,不用做任何转换,直接塞进 messages 数组就行。传统方案你得写转换函数,而且每种新字段都得加逻辑。
状态管理差异:多对话引擎 vs 单页状态
状态管理是架构层面的差异,决定了你的应用能做多复杂。
传统方案的状态管理:
typescript
// 传统方案:单页面状态,简单的 Pinia store
const useChatStore = defineStore('chat', () => {
const messages = ref<Message[]>([])
const isLoading = ref(false)
async function sendMessage(text: string) {
messages.value.push({ type: 'user', text })
isLoading.value = true
const response = await fetch('/api/chat')
// ... 解析响应
isLoading.value = false
}
return { messages, isLoading, sendMessage }
})
这种方案的问题:
- 只支持单会话------切换会话意味着清空消息或手写切换逻辑
- 切换会话时当前请求会被中断------没有"后台继续运行"的概念
- 存储策略手写------LocalStorage 还是 IndexedDB?序列化怎么做?
- 并行处理不支持------不能同时跑两个对话
TinyRobot 的状态管理:
typescript
// TinyRobot:useConversation 多会话引擎
const {
conversations, // 会话列表(只包含元数据,不加载全部消息)
activeConversationId, // 当前会话ID
activeConversation, // 当前会话(包含 engine 实例)
createConversation, // 创建新会话
switchConversation, // 切换会话(当前请求可在后台继续)
deleteConversation, // 删除会话
saveMessages, // 保存消息
} = useConversation({
useMessageOptions: {
responseProvider, // AI 请求函数
},
autoSaveMessages: true,
autoSaveThrottle: 1000,
storage: localStorageStrategyFactory(), // 或 indexedDBStorageStrategyFactory()
})
TinyRobot 的状态管理设计解决了传统方案的所有痛点:
| 功能 | 传统方案 | TinyRobot |
|---|---|---|
| 多会话支持 | 手写切换逻辑 | useConversation 内置 |
| 并行请求 | 不支持 | 后台引擎池,切换时请求继续运行 |
| 存储策略 | 手写 LocalStorage/IndexedDB | 内置两种策略 + 自定义接口 |
| 数据加载 | 全量加载 | 懒加载(只加载元数据,按需加载消息) |
| 自动保存 | 手写 debounce | autoSaveMessages + autoSaveThrottle |
最核心的差异是并行请求支持。在 TinyRobot 中,每个会话拥有独立的 useMessage 引擎,切换会话时当前引擎不会被销毁------它继续在后台运行。这意味着用户可以在会话 A 等待 AI 回复的同时,切换到会话 B 开始新的对话。这在传统方案中是无法实现的。
具体功能对比表
把差异量化,一目了然:
| 功能维度 | 传统组件库(Element Plus / Ant Design Vue / TinyVue) | TinyRobot |
|---|---|---|
| 流式文本渲染 | 手写 SSE 解析 + Markdown 渲染 + 增量更新优化 | TrBubble + useMessage + sseStreamToGenerator |
| 思考过程展示 | 手写折叠逻辑 + 动画 + 数据提取 | thinkingPlugin 自动处理,Bubble 内置 Reasoning 渲染器 |
| 工具调用展示 | 手写 JSON 渲染 + 多轮调用状态管理 | toolPlugin + BubbleRenderers.Tool/Tools |
| Markdown 渲染 | 手写 v-html + markdown-it + dompurify | BubbleRenderers.Markdown 一行配置 |
| 消息分组 | 手写相同角色合并逻辑 | BubbleList 内置 consecutive/divider/custom 分组策略 |
| 自动滚动 | 手写 scroll 监听 + 用户手势打断检测 | BubbleList autoScroll 内置 |
| 输入联想 | 手写下拉组件 + 搜索过滤 + 键盘导航 | TrSender.suggestion() 一行配置 |
| @提及 | 手写特殊字符检测 + 列表弹出 + 删除逻辑 | TrSender.mention() 一行配置 |
| 模板输入 | 手写 contentEditable + 模板切换 + 字段聚焦 | TrSender.template() 一行配置 |
| 语音输入 | 手写浏览器 API / 第三方对接 | VoiceButton 组件,内置浏览器识别 + 自定义 handler |
| 文件上传 | 手写 drag/drop + file input + 大小限制 | UploadButton 组件 |
| 多会话管理 | 手写 Pinia store + 切换逻辑 | useConversation 内置 |
| 数据持久化 | 手写 LocalStorage / IndexedDB | 内置两种策略 + 自定义接口 |
| 并行请求 | 不支持 | 后台引擎池支持 |
| MCP 集成 | 没有 | McpAddForm + McpServerPicker + toolPlugin MCP SDK 对接 |
| 主题切换 | 手写 CSS 变量 + localStorage 持久化 | ThemeProvider + useTheme 内置 |
| 消息格式 | 自定义结构(需转换) | 直接兼容 OpenAI ChatMessage |
| 组件数量 | 60-80+ | 16(AI 专用) |
这个对比表的核心结论:传统组件库在 AI 场景中,几乎所有功能都需要手写。而 TinyRobot 把这些功能封装成了组件或工具函数,一行代码就能调用。
什么时候用哪个?明确的选择标准
不是所有项目都用 TinyRobot,也不是所有项目只用传统组件库。关键是场景匹配。
只用传统组件库的场景:
| 场景 | 推荐库 |
|---|---|
| 后台管理系统(CRUD) | Element Plus / TinyVue |
| 数据看板(图表 + 表格) | Ant Design Vue + ECharts |
| 传统表单页面 | Element Plus / TinyVue |
| 静态内容展示网站 | 任何传统库 |
只用 TinyRobot 的场景:
| 场景 | 说明 |
|---|---|
| AI 聊天机器人(ChatGPT 类) | 全链路 AI 交互 |
| AI 知识库问答 | 工具调用 + 来源展示 |
| AI 代码助手 | Markdown + 工具调用 |
| MCP 工具集成平台 | 原生 MCP 支持 |
两者组合使用的场景(最常见):
| 场景 | TinyRobot 负责 | 传统库负责 |
|---|---|---|
| 企业 OA + AI 助手 | 侧栏对话界面 | OA 主界面(表单、审批流) |
| 客服系统 + AI 回复 | 对话面板 | 工单管理、数据报表 |
| IDE + AI Copilot | 内嵌对话 | 项目管理、代码浏览 |
| AI 产品 + 管理后台 | AI 对话界面 | 用户管理、配置面板 |
组合使用的最佳实践:
ts
// main.ts - 双库共存
import '@opentiny/tiny-robot/dist/style.css' // TinyRobot 样式
import 'element-plus/dist/index.css' // Element Plus 样式(或 TinyVue)
两个库的 CSS 变量命名空间不同(TinyRobot 用 --tr-*,Element Plus 用 --el-*),不会冲突。组件前缀也不同(tr-* vs el-*),模板中可以直接混用:
vue
<template>
<!-- AI 对话区域 - TinyRobot -->
<TrContainer v-model:show="showChat">
<TrBubbleList :messages="messages" />
<template #footer>
<TrSender v-model="inputText" />
</template>
</TrContainer>
<!-- 管理区域 - Element Plus -->
<el-table :data="userList">
<el-table-column prop="name" label="用户名" />
<el-table-column prop="role" label="角色" />
</el-table>
</template>
没有任何冲突,两个库各司其职。
混用的坑:哪些不该混
虽然两个库可以共存,但有些地方不应该混用:
1. 不要用传统 Input 做对话输入框
el-input 没有 SSE 流式联想、没有模板输入、没有 @提及、没有语音输入、没有 Ctrl+Enter 快捷键控制。用它做 AI 输入框,你得手写所有这些功能,最终代码量比直接用 TrSender 多十倍。
2. 不要用传统 Card 做消息气泡
el-card 是静态渲染组件,不支持流式更新、思考折叠、工具调用展示、消息分组。用它做消息气泡,你得把所有 AI 交互逻辑写在外层。
3. 不要用传统 Dialog 做对话容器
el-dialog 没有"侧栏模式"概念,没有 footer 区域放置 Sender,没有全屏/侧栏切换。对话容器应该用 TrContainer。
4. 不要用传统 Collapse 做思考过程折叠
el-collapse 的折叠逻辑是手动的,不能自动匹配 reasoning_content 的"思考中→展开,思考完成→收起"行为。应该用 Bubble 内置的 Reasoning 渲染器。
简而言之:对话区域的所有组件,都应该用 TinyRobot;非对话区域的组件,用传统库。
这是最清晰的分工原则。两个库在各自擅长的领域工作,而不是互相抢对方的活。
写在最后
AI 组件库和传统组件库的差异,不是"谁更好"的问题,而是"谁更适合什么场景"的问题。传统组件库在 CRUD 场景无可替代,但 AI 对话场景不是 CRUD------数据模型、渲染机制、状态管理、交互模式全不一样。
TinyRobot 的价值不是"替代传统库",而是"补上传统库做不到的那一块"。在 AI 对话场景中,TinyRobot 把传统库需要手写的几百行代码,封装成了几行配置。这种差距,用过一次就体会到了。
所以下次做项目时,记住这个原则:对话区域用 TinyRobot,非对话区域用传统库。两者共存,各取所长,才是最高效的组合。
OpenTiny NEXT 是一套企业智能前端开发解决方案,以生成式 UI 和 WebMCP 两大核心技术为基础,对现有传统的 TinyVue 组件库、TinyEngine 低代码引擎等产品进行智能化升级,构建出面向 Agent 应用的前端 NEXT-SDKs、AI Extension、TinyRobot智能助手、GenUI等新产品,加速企业应用的智能化改造,实现AI理解用户意图自主完成任务。
欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~
OpenTiny 官网:opentiny.design/ TinyRobot 代码仓库:github.com/opentiny/ti... (欢迎star ⭐) TinyRobot skill源码:github.com/opentiny/ag... (欢迎 Star ⭐)