IMKit 事件与回调挂载点
IMKit 的事件分三层:IMKitFacade 的 ready/error(挂载生命周期)、组件的 on/off/emit(交互与发送结果)、IMLib 的 RIM.addEventListener(网络/消息/回执等底层事件)。理解三层边界,才能把“UI 反馈”和“业务副作用”放在正确的位置。
概念
| 层 | 入口 | 典型事件 |
|---|---|---|
| 门面 | IMKit.on/off/emit | ready、error,以及宿主自定义事件 |
| 组件 | instance.on/off | Messages:如 ChatView sendText/sendMedia/sendVoice/pending/sendFailed/readReceipt/streamStart/streamChunk/streamEnd |
| IMLib | client.addEventListener | connected/disconnect/messages/conversation/read_receipt/kicked/typing/stream_changed 等 15 个 |
- 组件事件只描述“UI 层发生了什么”,不会替代 SDK 的业务事件;例如
sendText只表示用户提交了文本,不代表服务端已入库。 - 门面的
ready在mount/update后触发,适合做骨架屏与埋点;error携带{error,component}。 - 同一个 handler 既可用于
on也可用于off;匿名函数无法精确移除。 - 组件内部对 handler 异常做了 try/catch 并 console.error,不会因一个监听器抛错阻断其它监听器。
推荐分层
UI 反馈(滚动、Toast、气泡状态)订阅组件事件;业务状态(会话未读、消息入库、踢下线)订阅 IMLib 事件;页面生命周期(骨架、错误边界)订阅门面事件。
前置条件
- 已按 快速开始 完成
init与connect,页面能收发消息。 - 使用
@ni-paas/im-web/imkit(浏览器原生 ESM,无第三方依赖);自定义代码只调用 IMKit/IMLib 公开入口。 - 已引入默认样式:调用
ensureStyles(),或自行加载 IMKit 样式表。 - 自托管版无“UI 主题商店/付费皮肤”;所有自定义均为本地代码(不适用项)。
步骤
- 挂载并监听门面
IMKit.setup({container})后IMKit.on("ready", handler);错误统一在error里上报。 - 监听组件交互
对 ChatView 监听
sendText/pending/sendFailed/readReceipt;对输入区监听send/typing/pickMention。 - 监听 IMLib 业务事件
用
client.addEventListener(Events.MESSAGES, handler)处理到达消息、Events.KICKED处理强制下线。 - 清理
卸载时
chat.destroy()(会解绑内部监听)并client.removeEventListener;门面用IMKit.off。
参数表
| 事件 | 所属 | 载荷(节选) |
|---|---|---|
ready | IMKitFacade | {container,component,instance} |
error | IMKitFacade / ChatView | {error,component} 或 {code,msg} |
sendText / sendMedia / sendVoice | ChatView | {text,result} / {kind,result,file} / {result,duration} |
pending / sendFailed | ChatView | {message} / {message,result} |
readReceipt / readDetail | ChatView | {messageIds,result} / {messageId} |
streamStart/Chunk/End | ChatView | {streamId,message,payload} |
select / longpress | ConversationListView | {conversation} |
connected/disconnect/kicked | IMLib | 连接状态载荷 |
messages/conversation/read_receipt | IMLib | 消息/会话/回执 |
示例
三层事件接线 + 清理
JavaScript
import RongIMLib, { Events } from '@ni-paas/im-web/rongim';
import { IMKit, ChatView, ConversationListView } from '@ni-paas/im-web/imkit';
// 1) 门面生命周期
function onReady(payload) {
document.getElementById('skeleton').hidden = true;
console.log('mounted', payload.component);
}
function onError(payload) {
console.error('imkit error', payload.error || payload);
toast('界面加载失败,请重试');
}
IMKit.on('ready', onReady);
IMKit.on('error', onError);
// 2) 组件交互
const chat = new ChatView({ conversationType: 1, targetId: 'bob', currentUserId: 'alice' });
chat.mount(document.getElementById('chat-host'));
const onSendText = ({ text, result }) => {
if (result && result.code === 0) scrollToBottom();
};
chat.on('sendText', onSendText);
chat.on('sendFailed', ({ message, result }) => toast('发送失败:' + (result && result.msg)));
chat.on('readReceipt', ({ messageIds }) => console.log('read sent', messageIds.length));
// 3) IMLib 业务事件
const onMessages = (payload) => {
const list = payload && payload.messages ? payload.messages : [];
for (const message of list) chat.appendMessage(message);
};
const onKicked = (payload) => {
toast('账号已在其它端登录');
location.reload();
};
client.addEventListener(Events.MESSAGES, onMessages);
client.addEventListener(Events.KICKED, onKicked);
// 4) 清理(SPA 路由卸载时)
function dispose() {
chat.off('sendText', onSendText);
chat.destroy();
client.removeEventListener(Events.MESSAGES, onMessages);
client.removeEventListener(Events.KICKED, onKicked);
IMKit.off('ready', onReady);
IMKit.off('error', onError);
}门面自定义事件用于跨组件通信
JavaScript
// 任意位置广播(例如“主题已切换”)
IMKit.emit('theme:changed', { mode: 'dark' });
// 其它组件订阅
const onTheme = ({ mode }) => document.documentElement.setAttribute('data-theme', mode);
IMKit.on('theme:changed', onTheme);
// 清理
IMKit.off('theme:changed', onTheme);常见问题
组件事件和 IMLib 事件该选哪个?
UI 反馈用组件事件;消息入库、未读、连接、踢下线等业务事实用 IMLib 事件。
为什么 sendText 成功但对方没收到?
sendText 只代表本端提交成功(含失败结果);对方是否收到看消息轨迹或 IMLib messages。
on 多次注册同一函数会重复触发吗?
不会。组件内部用 Set 去重;但不同函数实例会各自触发。
destroy 后事件还在吗?
组件会清理自身监听;IMLib 监听需你显式 removeEventListener。