TuffTransport API
TuffTransport API
概述
TuffTransport 是 Tuff 插件的下一代 IPC 通信系统,提供类型安全、高性能的消息传递,内置批量处理和流式传输支持。
TuffTransport 是当前插件开发唯一支持的 IPC 入口。旧 Channel API 仅作为历史迁移参考保留。
为什么选择 TuffTransport?
| 特性 | 传统 Channel | TuffTransport |
|---|---|---|
| 类型安全 | ❌ 字符串事件名 | ✅ 编译时类型检查 |
| 自动补全 | ❌ 无 IDE 支持 | ✅ 完整 IntelliSense |
| 批量处理 | ❌ 手动实现 | ✅ 自动请求合并 |
| 流式传输 | ❌ 不支持 | ✅ MessagePort 流 |
| 错误处理 | ❌ 通用错误 | ✅ 结构化错误类型 |
介绍
快速开始
import { useTuffTransport, CoreBoxEvents, StorageEvents } from '@talex-touch/utils/transport'
const transport = useTuffTransport()
// 发送类型安全的请求
await transport.send(CoreBoxEvents.ui.hide)
// 带参数的请求
const result = await transport.send(CoreBoxEvents.search.query, {
query: { text: 'hello' }
})
// 批量请求(自动合并)
const [theme, lang] = await Promise.all([
transport.send(StorageEvents.app.get, { key: 'theme' }),
transport.send(StorageEvents.app.get, { key: 'language' }),
])
核心概念
TuffEvent
TuffTransport 的每次通信都使用 TuffEvent —— 一个在编译时编码请求/响应类型的类型安全事件定义。
// ❌ 已退役:字符串事件名,无类型安全
channel.send('core-box:search:query', { text: 'hello' })
// ✅ TuffTransport:类型安全,自动补全
transport.send(CoreBoxEvents.search.query, { query: { text: 'hello' } })
// ↑ TypeScript 强制正确的参数类型
Event Builder
使用 defineEvent 构建器创建自定义事件:
import { defineEvent } from '@talex-touch/utils/transport'
// 定义类型安全的事件
const MyPluginEvents = {
data: {
fetch: defineEvent('my-plugin')
.module('data')
.event('fetch')
.define<{ id: string }, { name: string; value: number }>()
}
}
// 使用 - 完全类型安全!
const result = await transport.send(MyPluginEvents.data.fetch, { id: '123' })
console.log(result.name, result.value) // ✅ 类型安全访问
API 参考
useTuffTransport()
在插件渲染进程中获取 transport 实例。
import { useTuffTransport } from '@talex-touch/utils/transport'
const transport = useTuffTransport()
transport.send(event, payload?, options?)
发送请求并等待响应。
参数:
event- TuffEvent 实例(必填)payload- 匹配事件请求类型的参数options- 发送选项
选项:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
immediate | boolean | false | 跳过批量,立即发送 |
timeout | number | 10000 | 请求超时(毫秒) |
示例:
// 简单请求
await transport.send(CoreBoxEvents.ui.hide)
// 带参数的请求
const result = await transport.send(StorageEvents.app.get, { key: 'theme' })
// 立即发送(跳过批量)
await transport.send(StorageEvents.app.set,
{ key: 'urgent', value: true },
{ immediate: true }
)
// 自定义超时
await transport.send(SlowEvent, data, { timeout: 30000 })
transport.stream(event, payload, options)
通过 MessagePort 发起流式请求。
const controller = await transport.stream(
CoreBoxEvents.search.query,
{ query: { text: 'hello' } },
{
onData: (result) => {
console.log('收到数据:', result)
},
onEnd: () => {
console.log('流完成')
},
onError: (err) => {
console.error('流错误:', err)
}
}
)
// 需要时取消流
controller.cancel()
MessagePort 升级与回退
stream/on在允许的通道上会自动升级 MessagePort;不可用时回退到 channel。- 运行时开关:
TALEX_TRANSPORT_PORT_CHANNELS(逗号分隔事件名),置空可禁用。默认包含:ClipboardEvents.change、AppEvents.fileIndex.progress、CoreBoxEvents.search.update、CoreBoxEvents.search.end、CoreBoxEvents.search.noResults。 - 手动控制:
options.port = false强制走 channel;或配置options.port.timeoutMs/options.port.scope。
// 自定义端口超时
const controller = await transport.stream(
AppEvents.fileIndex.progress,
undefined,
{
onData: (data) => console.log(data),
port: { timeoutMs: 800 }
}
)
// 高级用法:主动升级端口(仅在需要时使用)
const handle = await transport.openPort({
channel: CoreBoxEvents.search.update.toEventName(),
scope: 'window'
})
handle?.port.postMessage({ type: 'data', payload: { hello: 'world' } })
transport.on(event, handler)
注册事件处理器接收消息。
const cleanup = transport.on(SomeEvent, (payload) => {
console.log('收到:', payload)
return { success: true } // 返回响应
})
// 完成时清理
onUnmounted(() => cleanup())
transport.flush()
强制发送所有待处理的批量请求。
await transport.flush()
预定义事件
CoreBoxEvents
import { CoreBoxEvents } from '@talex-touch/utils/transport'
// UI 控制
CoreBoxEvents.ui.show // 显示 CoreBox
CoreBoxEvents.ui.hide // 隐藏 CoreBox
CoreBoxEvents.ui.expand // 展开/收起
// 搜索
CoreBoxEvents.search.query // 执行搜索(流式)
CoreBoxEvents.search.cancel // 取消搜索
// 输入框
CoreBoxEvents.input.get // 获取输入值
CoreBoxEvents.input.set // 设置输入值
CoreBoxEvents.input.clear // 清空输入
// 提供者
CoreBoxEvents.provider.deactivate // 停用提供者
CoreBoxEvents.provider.getDetails // 获取提供者详情(批量)
StorageEvents
import { StorageEvents } from '@talex-touch/utils/transport'
// 应用存储(支持批量)
StorageEvents.app.get // 获取值
StorageEvents.app.set // 设置值
StorageEvents.app.delete // 删除值
// 插件存储(支持批量)
StorageEvents.plugin.get // 获取插件值
StorageEvents.plugin.set // 设置插件值
PluginEvents
import { PluginEvents } from '@talex-touch/utils/transport'
// 生命周期
PluginEvents.lifecycle.load // 加载插件
PluginEvents.lifecycle.unload // 卸载插件
PluginEvents.lifecycle.reload // 重载插件
// 功能
PluginEvents.feature.trigger // 触发功能
// 日志(批量)
PluginEvents.log.write // 写入日志
BoxItemEvents
import { BoxItemEvents } from '@talex-touch/utils/transport'
// CRUD(支持批量)
BoxItemEvents.crud.create // 创建项
BoxItemEvents.crud.update // 更新项
BoxItemEvents.crud.upsert // 创建或更新
BoxItemEvents.crud.delete // 删除项
// 批量操作
BoxItemEvents.batch.upsert // 批量更新
BoxItemEvents.batch.delete // 批量删除
BoxItemEvents.batch.clear // 按来源清空
ClipboardEvents
import { ClipboardEvents } from '@talex-touch/utils/transport'
// 监听(流式)
ClipboardEvents.change // 订阅剪贴板变化
// 历史记录(支持批量)
ClipboardEvents.getHistory // 分页查询历史
ClipboardEvents.getLatest // 获取最新项
// 操作
ClipboardEvents.apply // 应用到活动应用
ClipboardEvents.delete // 删除历史项
ClipboardEvents.setFavorite // 切换收藏状态
ClipboardEvents.clearHistory // 清空历史
ClipboardEvents.getImageUrl // 获取历史图片原图地址
ClipboardEvents.write // 写入系统剪贴板
ClipboardEvents.read // 读取当前剪贴板快照
ClipboardEvents.readImage // 读取剪贴板图片
ClipboardEvents.readFiles // 读取剪贴板文件
ClipboardEvents.clear // 清空当前剪贴板
ClipboardEvents.copyAndPaste // 复制并粘贴
示例:订阅剪贴板变化
const controller = await transport.stream(
ClipboardEvents.change,
undefined,
{
onData: ({ latest, history }) => {
console.log('latest:', latest?.type, latest?.value)
console.log('history size:', history.length)
}
}
)
onUnmounted(() => controller.cancel())
示例:查询剪贴板历史
const { items, total } = await transport.send(ClipboardEvents.getHistory, {
page: 1,
pageSize: 20,
type: 'text',
keyword: '搜索关键词'
})
console.log(items[0]?.value, total)
批量处理
TuffTransport 自动合并支持批量的事件请求:
// 这 3 个请求自动合并为 1 次 IPC 调用
const [a, b, c] = await Promise.all([
transport.send(StorageEvents.app.get, { key: 'a' }),
transport.send(StorageEvents.app.get, { key: 'b' }),
transport.send(StorageEvents.app.get, { key: 'c' }),
])
// 结果:单次 IPC 包含 3 个请求,性能提升 500%+
批量配置
定义自定义事件时配置批量:
const MyEvent = defineEvent('my-plugin')
.module('data')
.event('fetch')
.define<Request, Response>({
batch: {
enabled: true,
windowMs: 50, // 收集 50ms
maxSize: 20, // 每批最多 20 个请求
mergeStrategy: 'dedupe' // 'queue' | 'dedupe' | 'latest'
}
})
合并策略:
queue- 所有请求按顺序处理dedupe- 相同参数的请求共享响应latest- 相同 key 只保留最新请求
流式传输
对于大数据或持续数据,使用 MessagePort 流式传输:
const MyStreamEvent = defineEvent('my-plugin')
.module('data')
.event('stream')
.define<{ filter: string }, AsyncIterable<DataItem>>({
stream: {
enabled: true,
bufferSize: 100
}
})
// 消费流
const controller = await transport.stream(
MyStreamEvent,
{ filter: 'active' },
{
onData: (item) => items.push(item),
onEnd: () => console.log('完成'),
onError: (err) => console.error(err)
}
)
// 需要时取消
onUnmounted(() => controller.cancel())
错误处理
TuffTransport 提供结构化错误:
import { TuffTransportError, TuffTransportErrorCode } from '@talex-touch/utils/transport'
try {
await transport.send(SomeEvent, data)
} catch (err) {
if (err instanceof TuffTransportError) {
switch (err.code) {
case TuffTransportErrorCode.TIMEOUT:
console.log('请求超时')
break
case TuffTransportErrorCode.INVALID_EVENT:
console.log('无效事件 - 请使用 defineEvent()')
break
case TuffTransportErrorCode.UNKNOWN_EVENT:
console.log('未注册处理器')
break
}
}
}
错误码:
| 错误码 | 说明 |
|---|---|
INVALID_EVENT | 不是有效的 TuffEvent |
UNKNOWN_EVENT | 未注册处理器 |
TIMEOUT | 请求超时 |
STREAM_CANCELLED | 流被取消 |
BATCH_FAILED | 批量请求失败 |
SERIALIZE_FAILED | 参数序列化失败 |
从传统 Channel 迁移
之前(传统方式)
import { useChannel } from '@talex-touch/utils/plugin/sdk'
const channel = useChannel()
const result = await channel.send('core-box:search:query', { text: 'hello' })
之后(TuffTransport)
import { useTuffTransport, CoreBoxEvents } from '@talex-touch/utils/transport'
const transport = useTuffTransport()
const result = await transport.send(CoreBoxEvents.search.query, { query: { text: 'hello' } })
硬切迁移
不要让新插件代码继续停留在 Channel 路径。请在同一次变更中把字符串事件发送替换为 typed TuffTransport 事件:
await transport.send(StorageEvents.app.get, { key: 'theme' })
技术原理
- 事件通过
defineEvent().module().event().define()构建类型约束,编译期校验请求/响应。 - 批量处理在客户端聚合请求,按策略合并并在主进程侧拆分。
- 流式传输基于 MessagePort,适用于持续数据推送场景。
最佳实践
- 始终使用 TuffEvent - 不要传字符串给
transport.send() - 定义自定义事件 - 使用
defineEvent()创建插件特定的通信 - 利用批量处理 - 使用
Promise.all()发起多个请求 - 清理处理器 - 在
onUnmounted()中调用清理函数 - 处理错误 - 检查
TuffTransportErrorCode进行特定处理 - 使用流式传输 - 用于大数据或实时更新
类型定义
interface ITuffTransport {
send<TReq, TRes>(
event: TuffEvent<TReq, TRes>,
payload: TReq,
options?: SendOptions
): Promise<TRes>
stream<TReq, TChunk>(
event: TuffEvent<TReq, AsyncIterable<TChunk>>,
payload: TReq,
options: StreamOptions<TChunk>
): Promise<StreamController>
on<TReq, TRes>(
event: TuffEvent<TReq, TRes>,
handler: (payload: TReq) => TRes | Promise<TRes>
): () => void
flush(): Promise<void>
destroy(): void
}
interface SendOptions {
immediate?: boolean
timeout?: number
}
interface StreamOptions<T> {
onData: (chunk: T) => void
onError?: (error: Error) => void
onEnd?: () => void
}
interface StreamController {
cancel(): void
readonly cancelled: boolean
readonly streamId: string
}