Web 快速接入
安装固定版本 SDK,通过业务后端提供的连接材料完成在线双向收发和退出清理。
本教程使用 2.0.5,让 Alice 与 Bob 在两个独立客户端中在线收发。连接由 WebSocket JSON-RPC CONNECT 完成鉴权。
1. 准备接入
准备以下条件:
- 使用支持原生 WebSocket、
TextEncoder与TextDecoder的现代浏览器; - 一个
/readyz正常且浏览器可访问 WebSocket Gateway 的 WuKongIM 单节点集群或多节点集群; - 一个同源或正确配置跨域策略的业务 BFF(面向客户端的业务后端);
- BFF 能在验证产品 Session 后返回当前用户的
uid、短期token与websocketUrl。
先阅读身份与 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 回调中看到消息;不要把发送结果直接渲染成对方“已读”或“已处理”。
- 打开两个独立浏览器 Profile 或两个隔离的浏览器上下文,分别登录 Alice 与 Bob;
- 两端都从各自的产品 Session 获取连接材料并观察
WKIMEvent.Connect; - Alice 向个人 Channel
bob发送消息,记录发送结果; - Bob 核对
fromUid、channelId、messageId和 Payload; - Bob 向 Alice 回发,验证反向链路;
- 模拟不可达地址,确认 10 秒内失败并
destroy,页面不会永久停在 Connecting; - 刷新、关闭或退出账号,确认旧实例已
off并destroy,没有重复连接。
5. 清理连接
退出账号、卸载应用或离开页面时调用 chat.stop(),用相同函数引用 off 后 destroy()。页面订阅者应移除自己的回调;连接所有者负责销毁实例。
6. 常见问题
- 页面提示 WebSocket 或编码 API 不存在:确认目标浏览器与构建环境提供 WebSocket、
TextEncoder、TextDecoder;不要从浏览器路径推断小程序支持。 - 连接前就被拒绝:检查 BFF 返回的 WSS 地址、Token 时效、证书、代理 Upgrade 和 Gateway 可达性。
- 刷新后重复收消息:不要在每次组件渲染时调用
on;保留回调并在卸载时off/destroy。 - Console 仍出现 Token 或 Payload:先用锁文件与浏览器 bundle 确认实际打入
easyjssdk@2.0.5,再检查应用事件回调、错误处理和采集器是否记录了完整对象。SDK 诊断默认关闭,显式开启时也只应输出脱敏运行元数据;用 npm 正式产物复现并保留低敏证据后再提交问题。
下一步
继续阅读消息收发与上线检查。需要离线恢复、会话、未读或推送时,先查看 SDK 选择。版本与验证记录保留各次验证的完整环境和范围。