新手避坑:使用 TinyRobot 入门阶段常见误区总结

新手避坑:使用 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.1pnpm 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 兼容性。使用时需要:

  1. 确保使用 >= 0.2.11 版本
  2. DropdownMenu 等组件默认不再使用 Teleport to="body"
  3. 如果仍有问题,检查是否有组件的 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 ⭐)

相关推荐
嘟嘟07171 小时前
二叉树从入门到实战:四大遍历 + 递归思想详解
前端
渣波1 小时前
全栈开发的“影分身”之术(mock):别再手动造数据了,你的 CRUD 不配让我等!
前端·javascript
亿元程序员1 小时前
小伙伴说这个撕胶带游戏很火很解压,于是我连夜做了一个Cocos教程...
前端
如果超人不会飞1 小时前
一文读懂 TinyRobot:前端 AI 组件库定位、价值与适用场景
前端·vue.js
如果超人不会飞1 小时前
用TinyRobot Welcome组件打造贴心的AI助手欢迎页
前端·vue.js
悟空瞎说1 小时前
Compose内嵌Flutter混合开发详解:页面嵌入、引擎缓存与双向通信完整实战
前端
如果超人不会飞1 小时前
TinyRobot DragOverlay轻松实现AI对话中的拖拽上传
前端·vue.js
elirlove11 小时前
打造属于自己的网页工匠台:HTML在线编辑器技术深度解析
前端·编辑器·html
wh_xmy1 小时前
从HTML5到AI,我的前端十年
前端·程序人生·十年程序员·ai 对前端的影响