> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript/events-and-message-handling.md).

# 在 TypeScript 中处理事件和消息

订阅 Convai Web SDK 客户端的连接、状态和消息事件，并在无框架页面中渲染对话。

订阅客户端的事件，以保持无需框架的 UI 与对话同步。当你自己渲染状态、消息或实时转录，而不是使用聊天组件时，请使用此页面。

### 先决条件

* 一个 `ConvaiClient` 如所示创建 [从原生 TypeScript 开始](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript.md).

### 订阅和取消订阅

`client.on(event, callback)` 返回一个用于移除该订阅的函数。 `client.off(event, callback)` 通过引用将其移除。

```typescript
const unsubscribe = client.on('message', handleMessage);

// 之后
unsubscribe();
// 或者
client.off('message', handleMessage);
```

在调用之前先订阅 `connect()` 这样你就不会错过会话的首批事件。

### 跟踪连接状态

`stateChange` 每当以下对象的任何字段发生变化时触发： `ConvaiClientState` 发生变化。 `state.agentState` 将活动标志合并为一个值： `'disconnected'`, `'connected'`, `'listening'`, `'thinking'`，或 `'speaking'`.

```typescript
import type { ConvaiClientState } from '@convai/web-sdk/vanilla';

client.on('stateChange', (state: ConvaiClientState) => {
  statusEl.textContent = state.isConnected ? state.agentState : 'disconnected';
});
```

`client.state` 按需返回相同的状态。

### 发送前先等待角色就绪

`connect` 当传输已就绪时触发。角色可能还需要稍等片刻才能准备好，而且 `sendUserTextMessage` 在传输尚未就绪时不会执行任何操作。请在以下时机发送第一条消息： `botReady`，或者检查 `client.isBotReady`.

```typescript
client.on('connect', () => console.log('Transport connected'));

client.on('botReady', () => {
  client.sendUserTextMessage('Hello!');
});

client.on('disconnect', (reason) => {
  console.log('Disconnected with reason', reason);
});
```

### 渲染对话

`message` 会随最新的 `ChatMessage`. `messagesChange` 会随整个数组触发，包括对仍在流式传输中的消息的更新。对于聊天记录，请从 `messagesChange` 并按以下条件过滤： `type`:

```typescript
import type { ChatMessage } from '@convai/web-sdk/vanilla';

const SHOWN = new Set(['user-llm-text', 'user-transcription', 'bot-llm-text', 'bot-output']);

client.on('messagesChange', (messages: ChatMessage[]) => {
  chatEl.replaceChildren(
    ...messages
      .filter((m) => SHOWN.has(m.type) && m.content.trim())
      .map((m) => {
        const div = document.createElement('div');
        div.className = m.type.startsWith('user') ? 'user' : 'bot';
        div.textContent = m.content;
        return div;
      }),
  );
});
```

聊天 UI 通常显示的消息类型：

| 类型                   | 内容                                                  |
| -------------------- | --------------------------------------------------- |
| `user-llm-text`      | 你通过以下方式发送的一条文本消息 `sendUserTextMessage`.             |
| `user-transcription` | 用户说的话，已从语音转写而来。最终片段会累积到同一条消息中，直到角色回复。               |
| `bot-llm-text`       | 角色的回复，在生成过程中流式传输。 `isStreaming` 是 `true` 直到它成为最终结果。 |
| `bot-output`         | 角色的回复，会在说出时逐步汇总。SDK 仅在该回复尚未以 `bot-llm-text`.        |

其他类型包含情绪、动作、空闲警告和无响应轮次。请参阅 [配置与类型参考](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript/best-practices-and-type-definitions.md#chatmessage) 完整列表。

### 显示实时转录

`userTranscriptionChange` 会在用户说话时触发，并携带截至目前的转录内容。当这段话语成为最终结果时，它会以空字符串触发。

```typescript
client.on('userTranscriptionChange', (text: string) => {
  liveTranscriptEl.textContent = text;
});
```

### 处理错误

`error` 会在连接失败以及角色未能按时报告就绪时触发。其负载就是抛出的任何内容，因此在读取之前请先检查类型 `message`.

```typescript
client.on('error', (error: unknown) => {
  console.error(error instanceof Error ? error.message : error);
});
```

### 后续步骤

{% content-ref url="/pages/8c6e3631e7e7da5b3c9acf5a0019d7737dc79e19" %}
[Web SDK 事件参考](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/event-reference.md)
{% endcontent-ref %}

{% content-ref url="/pages/548c1fa63be041ce888c5b5dc2a5a0f245175f1a" %}
[在 TypeScript 中构建自定义 UI](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript/building-a-custom-ui-typescript.md)
{% endcontent-ref %}

{% content-ref url="/pages/1ef8009eea712d4c41fd7a2a07e473a277395698" %}
[处理错误和断开连接](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/error-handling.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript/events-and-message-handling.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
