WuKongIM Docs

Web 快速接入

安装固定版本 SDK,通过业务后端提供的连接材料完成在线双向收发和退出清理。

编辑此页报告文档问题

本教程使用 2.0.5,让 Alice 与 Bob 在两个独立客户端中在线收发。连接由 WebSocket JSON-RPC CONNECT 完成鉴权。

1. 准备接入

准备以下条件:

  • 使用支持原生 WebSocket、TextEncoderTextDecoder 的现代浏览器;
  • 一个 /readyz 正常且浏览器可访问 WebSocket Gateway 的 WuKongIM 单节点集群或多节点集群;
  • 一个同源或正确配置跨域策略的业务 BFF(面向客户端的业务后端);
  • BFF 能在验证产品 Session 后返回当前用户的 uid、短期 tokenwebsocketUrl

先阅读身份与 Token。生产页面与 WebSocket 使用 HTTPS/WSS,Token 不写入 URL、Local Storage、日志或分析事件。

想先看到实际收发效果,可按运行官方示例准备两端;下文说明如何接入自己的应用。

默认设备类别为 WEB 1;业务后端保存 Token 时使用相同的 device_flag。APP 为 0,WEB 为 1,PC 为 2

2. 安装 SDK

在现有 Vite、Next.js 或其他打包工程中执行:

npm install --save-exact easyjssdk@2.0.5

提交 package-lock.json。本教程不使用 CDN 的浮动版本,避免页面刷新后静默更换 SDK。

3. 连接与监听

下面的 /api/im/bootstrap 是你的产品接口,不是 WuKongIM Product HTTP。BFF 在服务端验证登录态、签发短期 Token,并安全选择 /route 返回的 WebSocket 地址:

interface IMBootstrap {
  uid: string;
  token: string;
  websocketUrl: string;
}

async function fetchIMBootstrap(): Promise<IMBootstrap> {
  const response = await fetch('/api/im/bootstrap', {
    credentials: 'include',
    cache: 'no-store',
    headers: { accept: 'application/json' },
  });
  if (!response.ok) throw new Error(`IM bootstrap failed: ${response.status}`);
  return response.json() as Promise<IMBootstrap>;
}

浏览器不能直接调用 /user/token/route。这些接口的管理边界、服务端凭据和响应筛选都留在 BFF。

on 接收函数引用,off 必须拿到同一个引用。下面使用非全局单例模式,避免热更新、测试或多实例页面静默销毁另一个客户端:

import { WKIM, WKIMChannelType, WKIMEvent, type RecvMessage } from 'easyjssdk';

export class EasyChatClient {
  private im?: WKIM;
  constructor(private readonly receiveMessage: (message: RecvMessage) => void) {}

  private readonly onConnect = (_result: unknown) => {
    console.info('EasySDK connected');
  };

  private readonly onDisconnect = (_info: unknown) => {
    console.info('EasySDK disconnected');
  };

  private readonly onMessage = (message: RecvMessage) => {
    this.receiveMessage(message);
  };

  private readonly onError = (_error: unknown) => {
    console.error('EasySDK operation failed');
  };

  async start(bootstrap: IMBootstrap): Promise<void> {
    this.stop();

    const im = WKIM.init(
      bootstrap.websocketUrl,
      { uid: bootstrap.uid, token: bootstrap.token },
      { singleton: false, debugLogging: false },
    );
    im.on(WKIMEvent.Connect, this.onConnect);
    im.on(WKIMEvent.Disconnect, this.onDisconnect);
    im.on(WKIMEvent.Message, this.onMessage);
    im.on(WKIMEvent.Error, this.onError);

    this.im = im;
    await this.connectWithin(im, 10_000);
  }

  private async connectWithin(im: WKIM, timeoutMs: number): Promise<void> {
    let timeout: ReturnType<typeof setTimeout> | undefined;
    try {
      await Promise.race([
        im.connect(),
        new Promise<void>((_, reject) => {
          timeout = setTimeout(
            () => reject(new Error(`EasySDK connect exceeded ${timeoutMs} ms`)),
            timeoutMs,
          );
        }),
      ]);
    } catch (error) {
      im.destroy();
      if (this.im === im) this.im = undefined;
      throw error;
    } finally {
      if (timeout) clearTimeout(timeout);
    }
  }

  async sendText(targetUid: string, text: string) {
    if (!this.im?.isConnected) throw new Error('EasySDK is not connected');

    return this.im.send(targetUid, WKIMChannelType.Person, {
      type: 1,
      version: 1,
      content: text,
    });
  }

  stop(): void {
    const im = this.im;
    if (!im) return;

    im.off(WKIMEvent.Connect, this.onConnect);
    im.off(WKIMEvent.Disconnect, this.onDisconnect);
    im.off(WKIMEvent.Message, this.onMessage);
    im.off(WKIMEvent.Error, this.onError);
    im.destroy();
    this.im = undefined;
  }
}

debugLogging: false 与 SDK 默认值相同,这里显式写出以便审查生产配置。只有在受控诊断窗口中才设置为 true;SDK 仍只会输出脱敏的运行元数据,应用自己的回调与采集器也不能记录完整事件、结果或 Payload。

应用通常只创建一个 EasyChatClient。SDK 的 CONNECT JSON-RPC 请求有内部超时,但 WebSocket 打开阶段仍可能悬挂;connectWithin 因此把完整等待限制为 10 秒,并在失败后 destroy。React 中可以在根级 Provider 持有它,并在 Effect 清理函数中调用 stop();Vue/Svelte 同样在组件或应用卸载钩子里释放。

4. 收发第一条消息

先在页面加入按钮和接收区。两端都连接成功后再点击发送;Bob 页面把目标 bob 改为 alice。界面只显示最近一条消息,不保留无界历史。

<button id="send-message" disabled>Send</button>
<pre id="received-message"></pre>
const output = document.querySelector<HTMLElement>('#received-message');
const button = document.querySelector<HTMLButtonElement>('#send-message');
if (!output || !button) throw new Error('Missing message UI');
button.disabled = true;
const chat = new EasyChatClient((message) => {
  output.textContent = JSON.stringify({
    fromUid: message.fromUid,
    payload: message.payload,
  });
});
window.addEventListener('pagehide', () => chat.stop(), { once: true });
try {
  await chat.start(await fetchIMBootstrap());
  button.disabled = false;
} catch {
  chat.stop();
  output.textContent = 'Connection failed';
}
button.onclick = async () => {
  try {
    // Bob sends to 'alice'. Click only after both clients are connected.
    await chat.sendText('bob', 'Hello from Web EasySDK');
    console.info('SEND completed');
  } catch {
    console.error('Send failed; check its outcome before retrying');
  }
};

Alice 的 Promise 成功只代表发送请求收到了服务端结果。Bob 仍需在独立页面的 WKIMEvent.Message 回调中看到消息;不要把发送结果直接渲染成对方“已读”或“已处理”。

  1. 打开两个独立浏览器 Profile 或两个隔离的浏览器上下文,分别登录 Alice 与 Bob;
  2. 两端都从各自的产品 Session 获取连接材料并观察 WKIMEvent.Connect
  3. Alice 向个人 Channel bob 发送消息,记录发送结果;
  4. Bob 核对 fromUidchannelIdmessageId 和 Payload;
  5. Bob 向 Alice 回发,验证反向链路;
  6. 模拟不可达地址,确认 10 秒内失败并 destroy,页面不会永久停在 Connecting;
  7. 刷新、关闭或退出账号,确认旧实例已 offdestroy,没有重复连接。

5. 清理连接

退出账号、卸载应用或离开页面时调用 chat.stop(),用相同函数引用 offdestroy()。页面订阅者应移除自己的回调;连接所有者负责销毁实例。

6. 常见问题

  • 页面提示 WebSocket 或编码 API 不存在:确认目标浏览器与构建环境提供 WebSocket、TextEncoderTextDecoder;不要从浏览器路径推断小程序支持。
  • 连接前就被拒绝:检查 BFF 返回的 WSS 地址、Token 时效、证书、代理 Upgrade 和 Gateway 可达性。
  • 刷新后重复收消息:不要在每次组件渲染时调用 on;保留回调并在卸载时 off/destroy
  • Console 仍出现 Token 或 Payload:先用锁文件与浏览器 bundle 确认实际打入 easyjssdk@2.0.5,再检查应用事件回调、错误处理和采集器是否记录了完整对象。SDK 诊断默认关闭,显式开启时也只应输出脱敏运行元数据;用 npm 正式产物复现并保留低敏证据后再提交问题。

下一步

继续阅读消息收发上线检查。需要离线恢复、会话、未读或推送时,先查看 SDK 选择版本与验证记录保留各次验证的完整环境和范围。

本页内容