新手避坑:使用 TinyRobot 入门阶段常见误区总结
别让小坑绊倒大项目!9 个 TinyRobot 入门最常见的误区,我替你踩过了。
用 TinyRobot 开发 AI 聊天界面,上手确实很快------3 个组件 + 1 个工具函数就能跑通 Demo。但在实际开发中,总会遇到一些"明明代码没报错,但就是不好使"的情况。这些坑往往不是逻辑错误,而是用法误区------你以为某个 API 是这么用的,但它其实需要另一种方式。
这篇文章汇总了 9 个最常见的 TinyRobot 入门误区,每个都附带具体场景、错误示例和正确方案。如果你刚开始用 TinyRobot,先看完这篇再动手,能帮你省下至少两天的调试时间。
坑位 1:忘记引入 style.css ------ 组件全变"裸 HTML"
症状:代码写好了,页面渲染了,但气泡没有圆角、没有阴影、没有背景色,Sender 输入框就是一坨光秃秃的 div。看起来就像一堆裸 HTML 元素堆在一起。
原因 :TinyRobot 的所有样式(包括布局、颜色、圆角、阴影、CSS 变量)都集中在一个 CSS 文件 @opentiny/tiny-robot/dist/style.css 中。如果你没有引入这个文件,组件只有结构,没有样式。
错误做法:
ts
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
// ❌ 没有引入样式文件!
const app = createApp(App)
app.mount('#app')
正确做法:
ts
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import '@opentiny/tiny-robot/dist/style.css' // ✅ 必须引入!
const app = createApp(App)
app.mount('#app')
关键要点:
- 无论使用按需引入还是全局引入,样式文件都必须全局引入
- 样式文件不是按需的,因为 CSS 变量和基础样式是所有组件共享的
- 引入顺序:样式必须在组件使用之前
坑位 2:混淆 v0.3.x 和 v0.4 的 API ------ 写对了语法但用的是旧版
症状 :按照文档写了 responseProvider,但 TypeScript 提示找不到这个参数;或者用了 client 参数,但实际装的是 v0.4 版本。
原因:TinyRobot v0.4 是一次重大升级,useMessage 和 useConversation 的 API 有 Breaking Changes:
- v0.3.x 的
client参数 → v0.4 改为responseProvider - v0.3.x 的 useConversation 的
client→ v0.4 改为useMessageOptions - v0.4 重构了 Sender/Bubble/Kit 的内部结构,引入了可插拔渲染器与插件体系
错误做法(混用旧版 API):
ts
// ❌ 这是 v0.3.x 的写法,在 v0.4 中已经不存在 client 参数了
const { messages, sendMessage } = useMessage({
client: myClient, // v0.4 没有 client 了!
})
正确做法(v0.4 API):
ts
// ✅ v0.4 使用 responseProvider
const { messages, sendMessage } = useMessage({
responseProvider: async (requestBody, abortSignal) => {
// 处理请求...
},
})
如果你正在从 v0.3.x 迁移到 v0.4 ,TinyRobot 提供了 SenderCompat 组件帮助平滑过渡。具体迁移方法请参考官方迁移文档。
关键要点:
- 安装前确认你要用的版本:
pnpm add @opentiny/tiny-robot@0.4.1或pnpm add @opentiny/tiny-robot@0.3.3 - v0.4 的 useMessage 从
@opentiny/tiny-robot-kit引入 - 不要把网上搜到的 v0.3.x 示例代码直接用到 v0.4 项目中
坑位 3:组件命名搞错 ------ Tr 前缀忘了或写法不一致
症状 :模板中写了 <Bubble> 或 <bubble>,页面报错 "Component Bubble is not resolved";或者 script 中写了 import Bubble from '@opentiny/tiny-robot',TypeScript 报错找不到。
原因 :TinyRobot 所有组件以 Tr 为前缀(Tr = TinyRobot 的缩写)。在 <script> 中引入时使用大驼峰命名(TrBubble),在 <template> 中使用小写连字符(<tr-bubble>)。这是 Vue 3 的标准命名转换规则。
错误做法:
vue
<template>
<!-- ❌ 缺少 Tr 前缀 -->
<bubble role="ai" content="hello" />
<!-- ❌ 大驼峰在模板中使用(Vue 3 支持但不推荐) -->
<TrBubble role="ai" content="hello" />
</template>
<script setup>
// ❌ 缺少 Tr 前缀
import { Bubble } from '@opentiny/tiny-robot'
</script>
正确做法:
vue
<template>
<!-- ✅ 模板中使用小写连字符 + tr 前缀 -->
<tr-bubble role="ai" content="hello" />
<tr-sender placeholder="输入消息..." />
</template>
<script setup>
// ✅ script 中使用大驼峰 + Tr 前缀
import { TrBubble, TrSender } from '@opentiny/tiny-robot'
</script>
组件命名对照表:
| Script 引入名 | 模板使用名 |
|---|---|
| TrBubble | <tr-bubble> |
| TrSender | <tr-sender> |
| TrContainer | <tr-container> |
| TrPrompts | <tr-prompts> |
| TrWelcome | <tr-welcome> |
| TrAttachments | <tr-attachments> |
| TrFeedback | <tr-feedback> |
| TrHistory | <tr-history> |
| ThemeProvider | <ThemeProvider> |
关键要点 :ThemeProvider 是特殊组件,模板中直接用 <ThemeProvider>,因为它不是 Tr 前缀。
坑位 4:useTheme 在 ThemeProvider 外面调用 ------ 返回 false 不报错
症状 :调用了 useTheme() 获取主题控制方法,但 setTheme() 和 toggleColorMode() 不生效,或者 useTheme() 直接返回 false。
原因 :useTheme 是依赖注入式的组合式函数,它必须在 ThemeProvider 包裹的组件内部调用。如果在 ThemeProvider 外面的组件调用,就找不到注入的上下文,返回 false。
错误做法:
vue
<!-- ❌ useTheme 在 ThemeProvider 外面调用 -->
<template>
<ThemeProvider>
<ChatApp />
</ThemeProvider>
</template>
<script setup>
// 在 ThemeProvider 同级或更外层调用 useTheme
import { useTheme } from '@opentiny/tiny-robot'
const { toggleColorMode } = useTheme() // ❌ 返回 false,不生效
</script>
正确做法:
vue
<!-- App.vue -->
<template>
<ThemeProvider>
<ChatApp />
</ThemeProvider>
</template>
<script setup>
import { ThemeProvider } from '@opentiny/tiny-robot'
import ChatApp from './ChatApp.vue'
</script>
vue
<!-- ChatApp.vue ------ ThemeProvider 包裹的子组件 -->
<template>
<button @click="toggleColorMode">切换主题</button>
</template>
<script setup>
import { useTheme } from '@opentiny/tiny-robot'
const { toggleColorMode } = useTheme() // ✅ 在 ThemeProvider 内部调用,正常工作
</script>
关键要点:
useTheme()必须在ThemeProvider包裹的子组件中调用- 主题切换逻辑放在内部组件,ThemeProvider 只负责包裹和注入
坑位 5:IME 输入法与 Sender 的冲突 ------ 中文输入按 Enter 误触发发送
症状:使用中文输入法时,打字过程中按 Enter 想选择候选词,结果消息被直接发送出去了。日文、韩文输入法也有同样的问题。
原因:这是 contentEditable 输入框的经典问题。TinyRobot v0.3+ 已经在底层处理了 IME compositionStart/compositionEnd 事件,但如果你自定义了提交逻辑或覆盖了快捷键行为,可能会绕过内置的 IME 保护。
错误做法:
vue
<!-- ❌ 手动监听 keydown 事件,绕过了 Sender 的 IME 保护 -->
<tr-sender @keydown.enter="handleSend" />
正确做法:
vue
<!-- ✅ 使用 Sender 的 @submit 事件,内置 IME 保护 -->
<tr-sender @submit="handleSend" />
Sender v0.4 的快捷键配置:
ts
// v0.4 的 Sender 基于 Tiptap,内置了快捷键控制
// 默认:Enter 发送,Ctrl+Enter / Shift+Enter 插入换行
// IME 输入时 Enter 不会触发发送
关键要点:
- 使用
@submit事件,不要手动监听keydown - Sender v0.4 基于 Tiptap 架构,IME 处理更可靠
- 如果遇到 IME 问题,先检查是否有自定义事件监听覆盖了默认行为
坑位 6:Shadow DOM 下 Teleport 挂载失败 ------ 弹出框不见了
症状:在 Shadow DOM 环境中使用 TinyRobot,DropdownMenu、Tooltip 等弹出类组件的浮层无法显示,或者浮层挂载到了主文档 body 上而非 Shadow DOM 内部。
原因 :TinyRobot 的弹出类组件内部使用了 Teleport 来挂载浮层。默认 Teleport to="body" 会将浮层挂载到主文档的 body 上,但如果你的应用运行在 Shadow DOM 中,浮层就脱离了 Shadow DOM 的样式隔离,导致样式丢失或位置错误。
解决方案:TinyRobot v0.2.11+ 已经支持 Shadow DOM 的 Teleport 兼容性。使用时需要:
- 确保使用 >= 0.2.11 版本
- DropdownMenu 等组件默认不再使用
Teleport to="body" - 如果仍有问题,检查是否有组件的
appendTo属性需要配置
vue
<!-- Shadow DOM 环境下使用 DropdownMenu -->
<tr-dropdown-menu :append-to="shadowHostElement" />
关键要点:
- Shadow DOM 环境下,确保 TinyRobot 版本 >= 0.2.11
- 弹出类组件提供
appendTo属性,可以指定挂载目标 - 测试 Shadow DOM 场景时,优先检查浮层组件是否正常显示
坑位 7:SenderCompat 的 template 与 block 类型混淆
症状:从 v0.3.x 迁移到 v0.4 时,使用 SenderCompat 组件,但模板编辑功能的行为不一致------有时候模板内容是字符串,有时候是结构化数据。
原因 :SenderCompat 是 v0.4 提供的兼容组件,帮助 v0.3.x 用户平滑过渡。它有两种输入模式:template(模板模式,结构化编辑)和 block(块模式,纯文本)。两者的数据处理方式不同:
- template 模式:内容是结构化的模板数据,包含字段、占位符等
- block 模式:内容是纯文本字符串,类似传统 textarea
错误做法:
vue
<!-- ❌ 用 template 模式提交纯文本 -->
<tr-sender-compat mode="template" @submit="handleSubmit" />
<script setup>
function handleSubmit(content: string) {
// template 模式下,content 不是纯文本字符串
// 而是包含模板结构的对象
sendMessage(content) // ❌ 传了错误的数据格式
}
</script>
正确做法:
vue
<!-- ✅ 根据实际需求选择 mode -->
<!-- 纯文本发送用 block 模式 -->
<tr-sender-compat mode="block" @submit="handleSubmit" />
<!-- 结构化模板编辑用 template 模式 -->
<tr-sender-compat mode="template" @submit="handleTemplateSubmit" />
<script setup>
function handleSubmit(content: string) {
// block 模式:content 是纯文本字符串
sendMessage(content)
}
function handleTemplateSubmit(templateData: any) {
// template 模式:templateData 是结构化数据
// 需要根据模板结构处理
}
</script>
关键要点:
- SenderCompat 的
mode属性决定内容数据格式 - 迁移时如果不需要模板编辑功能,直接用 v0.4 的 TrSender
- 如果需要模板编辑,确认数据处理逻辑匹配对应的 mode
坑位 8:存储策略选择不当 ------ LocalStorage 存多了就爆
症状:聊天记录越来越多,突然发现页面刷新后历史消息丢失了一部分,或者 localStorage 报错 "Quota exceeded"。
原因 :useConversation 默认使用 LocalStorage 存储策略,而 LocalStorage 的容量限制通常是 5-10MB。对于长对话、多会话的场景,5MB 很容易撑爆。
错误做法:
ts
// ❌ 默认 LocalStorage,大量数据会爆
const { conversations, activeConversation } = useConversation({
useMessageOptions: { responseProvider }
})
正确做法:
根据你的数据量选择合适的存储策略:
ts
// ✅ 大量数据使用 IndexedDB
import { indexedDBStorageStrategyFactory } from '@opentiny/tiny-robot-kit'
const { conversations, activeConversation } = useConversation({
useMessageOptions: { responseProvider },
storage: indexedDBStorageStrategyFactory({
dbName: 'my-chat-db',
}),
})
ts
// ✅ 小量数据用 LocalStorage(默认行为)
import { localStorageStrategyFactory } from '@opentiny/tiny-robot-kit'
const { conversations, activeConversation } = useConversation({
useMessageOptions: { responseProvider },
storage: localStorageStrategyFactory({
key: 'my-chat-data',
}),
})
ts
// ✅ 需要服务器持久化:自定义存储策略
const customStorage: ConversationStorageStrategy = {
async loadConversations() {
const res = await fetch('/api/conversations')
return res.json()
},
async loadMessages(id: string) {
const res = await fetch(`/api/conversations/${id}/messages`)
return res.json()
},
async saveConversation(conv: ConversationInfo) {
await fetch('/api/conversations', {
method: 'POST',
body: JSON.stringify(conv)
})
},
async saveMessages(id: string, messages: ChatMessage[]) {
await fetch(`/api/conversations/${id}/messages`, {
method: 'PUT',
body: JSON.stringify(messages)
})
},
async deleteConversation(id: string) {
await fetch(`/api/conversations/${id}`, { method: 'DELETE' })
},
}
存储策略对比:
| 策略 | 容量 | 性能 | 适用场景 |
|---|---|---|---|
| LocalStorage | 5-10MB | 快 | 少量数据、简单场景 |
| IndexedDB | 无限(磁盘空间) | 中 | 大量数据、长对话历史 |
| 自定义 | 取决后端 | 取决网络 | 服务器持久化、多端同步 |
关键要点:
- 默认是 LocalStorage,适合轻量场景
- 如果对话可能很长、会话数量多,一开始就用 IndexedDB
- 企业项目推荐自定义存储策略,将数据持久化到服务器
坑位 9:SSE 流式响应处理不当 ------ 数据丢失或解析错误
症状:接入真实 SSE API 后,AI 回复不完整、内容丢失、或者浏览器控制台报 JSON 解析错误。
原因 :SSE(Server-Sent Events)的数据格式是 data: {...}\n\n 的文本流,不能直接用 response.json() 解析。需要用 TinyRobot 提供的 sseStreamToGenerator 工具函数将 fetch 的 Response 转为 AsyncGenerator。
错误做法:
ts
// ❌ 直接用 fetch + json() 处理 SSE 流
async function responseProvider(requestBody: any, abortSignal: AbortSignal) {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ ...requestBody, stream: true }),
signal: abortSignal,
})
return response.json() // ❌ SSE 流不能用 json() 解析!
}
正确做法:
ts
// ✅ 使用 sseStreamToGenerator 处理 SSE 流
import { sseStreamToGenerator } from '@opentiny/tiny-robot-kit'
async function responseProvider(requestBody: any, abortSignal: AbortSignal) {
const response = await fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${import.meta.env.VITE_API_KEY}`,
},
body: JSON.stringify({ ...requestBody, stream: true }),
signal: abortSignal,
})
// sseStreamToGenerator 将 Response 转为 AsyncGenerator
return sseStreamToGenerator(response)
}
非流式 API 不需要 sseStreamToGenerator:
ts
// ✅ 非流式 API:直接返回完整 JSON
async function nonStreamingResponseProvider(requestBody: any, abortSignal: AbortSignal) {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ ...requestBody, stream: false }),
signal: abortSignal,
})
return response.json() // ✅ 非流式可以用 json()
}
SSE 数据格式与 ChatCompletion 的映射:
TinyRobot 的 useMessage 期望 SSE 流的每个 chunk 是 OpenAI 兼容的 ChatCompletion 格式:
json
{
"choices": [{
"delta": { "content": "你好" },
"finish_reason": null
}]
}
如果你的后端返回的格式不同(比如自定义的 SSE 格式),需要在 sseStreamToGenerator 之前做格式转换,或者使用 onCompletionChunk 钩子自定义处理逻辑。
关键要点:
- 流式 API 必须使用
sseStreamToGenerator - 非流式 API 直接返回
response.json() - SSE chunk 格式需要兼容 OpenAI ChatCompletion 结构
- 如果后端格式不同,用
onCompletionChunk自定义处理
总结:9 个坑位速查表
| 坑位 | 症状 | 核心原因 | 一句话解决方案 |
|---|---|---|---|
| 1 | 组件无样式 | 没引入 style.css | import '@opentiny/tiny-robot/dist/style.css' |
| 2 | v0.3/v0.4 API 混用 | 版本 API 不兼容 | 确认版本,用 v0.4 的 responseProvider |
| 3 | 组件名找不到 | Tr 前缀缺失 | 组件用 TrXxx / <tr-xxx> |
| 4 | useTheme 返回 false | 在 ThemeProvider 外调用 | 在 ThemeProvider 子组件中调用 |
| 5 | IME 输入误发送 | 绕过 Sender 内置 IME 保护 | 用 @submit 事件而非 keydown |
| 6 | Shadow DOM 浮层丢失 | Teleport 挂载位置错误 | 版本 >= 0.2.11,配置 appendTo |
| 7 | SenderCompat 数据格式错 | template/block 混淆 | 根据 mode 选择数据处理方式 |
| 8 | 存储容量爆满 | 默认 LocalStorage 太小 | 大数据用 IndexedDB 或自定义存储 |
| 9 | SSE 流解析错误 | 没用 sseStreamToGenerator | 流式用 sseStreamToGenerator |
记住这 9 个坑位,TinyRobot 入门阶段 99% 的"莫名其妙不工作"问题都能快速定位。如果你遇到了不在列表中的问题,欢迎到 GitHub Issues 反馈,社区会帮你解决。
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 ⭐)