Converse.js 14.0.1 嵌入集成使用指南
把 Converse.js 14.0.1 嵌入自有网站并打通自建 XMPP:三种 view_mode 形态、v14 插件机制与 api 调用面、Prosody 0.12 联调全流程,含六个实测踩坑(ESM/模块覆盖/跨源403/端点发现/WebCrypto/插件白名单)排障实录。
Converse.js 14.0.1 嵌入集成使用指南
目标:把 Converse.js 14.0.1 嵌入自己的网站并与自建 XMPP 服务端打通——三种
view_mode嵌入形态、v14 插件机制与api.*调用面、Prosody 0.12 联调全流程,含六个实测踩坑(ESM 加载、模块覆盖、跨源 403、端点发现阻断、WebCrypto 安全上下文、插件白名单)的完整排障实录。已在 Ubuntu 22.04(nginx/1.18.0 + Prosody 0.12.1)+ Chromium 实测通过,消息收发全链路成功。
一、环境信息
本文所有命令、页面与回显均在以下实测环境中执行:
- 操作系统:Ubuntu 22.04(x86_64)
- Converse.js:14.0.1(npm 包 dist 自托管,对照组用官方 CDN
cdn.conversejs.org/14.0.1/dist/) - Web 服务器:nginx/1.18.0(托管 dist 与示例页面)
- XMPP 服务端:Prosody 0.12.1(apt 安装,自带
mod_websocket/mod_bosh,监听 5280) - 演示账号:
admin@localhost/q1w2e3r4 - 验证浏览器:Chromium(无头模式,CDP 驱动采集 DOM/控制台/诊断输出)
💡 说明:嵌入 Converse.js 的本质是「宿主页面引入一个 CSS + 一个 ES Module JS,再调一次
converse.initialize()」。聊天逻辑全部运行在访客浏览器里,你的 Web 服务器只分发静态文件,消息经由 XMPP 服务器路由。
二、前置条件
- 一个可连的 XMPP 服务端,且开启 WebSocket 或 BOSH 传输。本文用 Prosody 0.12(搭建的完整流程见《Ubuntu 22.04 安装 Prosody IM 13.0.7 指南》,本文 5.1 给出最小可用配置);
- 一个你能改 HTML 的宿主页面,以及 Converse.js 14.0.1 的 dist 产物(自托管见《Ubuntu 22.04 安装 Converse.js 14 指南》,编排化部署见《Converse.js Docker Compose 部署指南》;临时验证可直接用官方 CDN);
- HTTPS 或 localhost 访问环境:SCRAM 密码认证依赖浏览器 WebCrypto(
crypto.subtle),仅安全上下文可用,否则登录直接失败(踩坑实录见 5.1 步骤 6)。
三、依赖引入
在宿主页面 <head> 引入样式与脚本。v14 产物是原生 ES Module,JS 必须 type="module"(用传统 <script src> 会得到 converse is not defined 且页面空白,排障细节见《Ubuntu 22.04 安装 Converse.js 14 指南》9.1 节):
自托管方式(dist 挂在站点 /dist/ 子路径):
<link rel="stylesheet" href="/dist/converse.min.css">
<script type="module" src="/dist/converse.min.js"></script>
CDN 方式(带版本号锁定):
<link rel="stylesheet" href="https://cdn.conversejs.org/14.0.1/dist/converse.min.css">
<script type="module" src="https://cdn.conversejs.org/14.0.1/dist/converse.min.js"></script>
验证引入成功:浏览器控制台 typeof converse 应返回 "object",页面 DOM 中出现 <converse-root> 元素。实测自托管与 CDN 两种引入最终 DOM 状态一致:converse-root=true, controlbox=true。
四、核心 API 与配置
4.1 三种嵌入形态(view_mode)
| view_mode | 形态 | 典型场景 |
|---|---|---|
fullscreen | 铺满整页,自带背景 | 独立 Web 聊天站(安装篇默认形态) |
overlayed | 页面右下角浮出聊天窗,可拖大拖小 | 给既有网站加客服/社区聊天入口(嵌入首选) |
embedded | 聊天窗嵌入页面指定区域(配合 root 可挂进 shadow DOM) | 文档页/商品页旁路讨论区 |
本文示例以 overlayed 为主线(5.1/5.2),embedded 见 5.3,fullscreen 即安装篇的形态。
4.2 面向嵌入的关键初始化参数
| 参数 | 嵌入场景建议值 | 作用 |
|---|---|---|
view_mode | overlayed / embedded | 见 4.1 |
singleton | true | 只允许一个会话窗,嵌入场景防窗口铺屏 |
allow_url_history_change | false | 不让 Converse 用 History API 改宿主页面地址栏 |
locked_domain | 你的 XMPP 域 | 锁定登录域,用户只输用户名 |
discover_connection_methods | false(域名不可解析时必须) | 关闭 XEP-0156 端点发现,见 5.1 踩坑 |
auto_login | 视场景 | 配合凭据来源实现免登 |
show_controlbox_by_default | 嵌入时默认 false | 是否默认展开控制框 |
i18n | zh_CN | 中文界面 |
whitelisted_plugins | 自定义插件名列表 | 自定义插件必须列入才加载(v14 实测) |
全部 153 个参数见《Converse.js 14 配置参数详解》。
4.3 v14 插件机制与 API 面
Converse.js 的核心功能本身就是一组插件,对外扩展走同一机制。v14 的两个关键事实(均实测确认):
- 全局
converse对象上没有.api——只有initialize、plugins、env、keycodes等少量成员;完整 API 必须在自定义插件内部经this._converse.api获取; - 自定义插件必须列入
whitelisted_plugins才会被加载,否则initialize()不会触发。
插件骨架:
converse.plugins.add('myplugin', {
initialize() {
const api = this._converse.api; // v14 唯一 API 入口
const { $msg, $iq, $pres, Strophe, dayjs } = converse.env; // Stanza 构造工具
api.listen.on('connected', () => console.log('connected'));
}
});
converse.initialize({
// ...其他配置
whitelisted_plugins: ['myplugin']
});
常用 API 域(v14,与官方 plugin-development 文档一致):
| API | 作用 |
|---|---|
api.listen.on(event, handler) / api.listen.off | 事件订阅(connected/disconnected/message 等) |
api.user.jid() | 当前登录 JID |
api.send(stanza) | 发送 Stanza(用 converse.env.$msg 等构造) |
api.chats.open(jid) | 打开与某 JID 的单聊窗口 |
api.rooms.open(jid) | 打开/加入群聊 |
api.settings.extend/get | 读取/扩展运行时配置 |
api.promises.add / api.waitUntil | 生命周期等待点 |
api.archive.query | MAM 归档查询 |
api.trigger | 触发自定义事件 |
api.registerMessageInterceptor | 注册消息拦截器(改写/过滤收发消息) |
五、集成示例
5.1 全链路示例:overlayed 浮窗 + Prosody 联调(可独立执行)
目标:在一台 Ubuntu 22.04 机器上,从 Prosody 配置到浏览器收发消息全部跑通。浏览器、nginx、Prosody 同机(故示例用 localhost 满足安全上下文);生产环境把 localhost 换成实际域名并全程 HTTPS。
步骤 1:Prosody 最小可用配置
apt 安装的 Prosody 0.12 自带 mod_websocket/mod_bosh(/usr/lib/prosody/modules/),无需另装。编辑 /etc/prosody/prosody.cfg.lua,在全局段(VirtualHost 之前)的 modules_enabled 表中启用两个传输模块,并补四条全局选项:
modules_enabled = {
-- ...保留原有条目...
"websocket";
"bosh";
}
allow_registration = true -- 允许带内注册(演示用,生产关掉)
consider_websocket_secure = true -- 反向代理终结 TLS 后仍按安全连接对待
cross_domain_websocket = true -- 允许跨源 WebSocket(嵌入页面与 XMPP 端口不同源)
cross_domain_bosh = true
⚠️ 踩坑实录 ①(模块覆盖):Ubuntu 包默认在
prosody.cfg.lua末尾(VirtualHost "localhost"之后)用Include "conf.d/*.cfg.lua"引入附加配置。若在 conf.d 里再写modules_enabled,会整体覆盖 localhost 域的模块表,登录时服务器回No stream features to offer(无任何流特性可提供)。正确做法就是上面这样:传输模块写进主配置全局模块表。
重启并注册用户:
systemctl restart prosody
prosodyctl register admin localhost q1w2e3r4
步骤 2:验证 WebSocket 握手
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Sec-WebSocket-Protocol: xmpp" \
http://127.0.0.1:5280/xmpp-websocket
实测回显(返回 101 后连接挂起,即为成功;Ctrl+C 结束):
HTTP/1.1 101
⚠️ 踩坑实录 ②(跨源 403):
cross_domain_websocket默认只允许同源握手。嵌入页面(如http://localhost:80)与 WebSocket 端点(http://localhost:5280)端口不同即跨源,不配cross_domain_websocket = true时握手直接 403(对应mod_websocket.lua的check_origin校验)。
步骤 3:宿主页面
保存为 /var/www/conversejs/demo.html(dist 已按安装篇挂在 /dist/):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>站内嵌入 Converse.js</title>
<link rel="stylesheet" href="/dist/converse.min.css">
<script type="module" src="/dist/converse.min.js"></script>
</head>
<body>
<h1>我的网站页面</h1>
<p>右下角是嵌入的聊天窗。</p>
<script type="module">
converse.initialize({
websocket_url: "ws://localhost:5280/xmpp-websocket",
view_mode: "overlayed",
authentication: "login",
discover_connection_methods: false, // 关键:JID 域 localhost 不可解析,关端点发现
i18n: "zh_CN",
singleton: true,
allow_url_history_change: false
});
</script>
</body>
</html>
步骤 4:登录
浏览器访问 http://localhost/demo.html,右下角浮窗展开登录框,输入 admin@localhost / q1w2e3r4 登录。
⚠️ 踩坑实录 ③(端点发现阻断登录):
discover_connection_methods默认true,登录前会先请求https://localhost/.well-known/host-meta(XEP-0156)自动发现端点。localhost 上没有这个 HTTPS 服务,浏览器直接抛TypeError: Failed to fetch并阻断登录流程。置false后登录立通。生产环境若域名可解析且正确部署了 host-meta,可保持默认。
步骤 5:确认登录成功
浏览器控制台实测回显:
SASL authentication succeeded.
Prosody 服务端日志同步确认:
Authenticated as admin@localhost
步骤 6:为什么强调 localhost / HTTPS
⚠️ 踩坑实录 ④(WebCrypto 安全上下文):同一页面若改用
http://<局域网IP>/demo.html访问(非 localhost),提交登录后控制台报Cannot read properties of undefined (reading 'deriveBits'),SASL 失败——SCRAM 认证的密钥派生走crypto.subtle,该 API 只在安全上下文(HTTPS、localhost、127.0.0.1)暴露。生产环境必须全站 HTTPS(页面 +wss://端点);实验环境用 localhost 访问即满足条件。
5.2 插件示例:事件订阅 + 编程收发消息
在 5.1 页面基础上加自定义插件(务必加 whitelisted_plugins),登录后自动给自己发一条消息并监听回执:
<script type="module">
converse.plugins.add('e1test', {
initialize() {
const api = this._converse.api;
console.log('DIAG: plugin e1test initialized');
api.listen.on('connected', () => {
console.log('EVENT connected, jid=' + api.user.jid());
// 登录成功后:给自己发一条消息
const { $msg } = converse.env;
api.send($msg({ to: 'admin@localhost', type: 'chat' })
.c('body').t('Hello Converse E1'));
console.log('message sent via api.send');
// 打开与自己的会话窗
api.chats.open('admin@localhost').then(c => console.log('api.chats.open ok, chat id=' + c.get('jid')));
});
api.listen.on('message', (m) => {
console.log('EVENT message: from=' + m.get('from') + ' body=' + m.get('body'));
});
}
});
converse.initialize({
websocket_url: "ws://localhost:5280/xmpp-websocket",
view_mode: "overlayed",
discover_connection_methods: false,
i18n: "zh_CN",
whitelisted_plugins: ['e1test']
});
</script>
登录 admin@localhost / q1w2e3r4 后,浏览器控制台实测回显(逐行照抄):
DIAG: plugin e1test initialized
EVENT connected, jid=admin@localhost/converse.js-32168925
message sent via api.send
api.chats.open ok, chat id=admin@localhost
EVENT message: from=admin@localhost/converse.js-32168925 body=Hello Converse E1
JID 尾部的 converse.js-32168925 是 Converse.js 自动生成的资源名;给自己发消息立即被服务端路由回来,证明「连接 → 认证 → 发送 → 接收」全链路打通。
⚠️ 踩坑实录 ⑤(插件白名单):v14 实测,去掉
whitelisted_plugins: ['e1test']后插件的initialize()完全不触发(控制台无DIAG: plugin e1test initialized)。这是与老版本行为最容易踩的差异之一。踩坑实录 ⑥(ESM 加载):本页两个
<script>都必须type="module";写成传统 script 时typeof converse === 'undefined'、<converse-root>不进 DOM,页面空白(与安装篇 9.1 同一坑,嵌入场景同样高发)。
CDN 对照组:同一份插件页面仅把引入换成 CDN 两行,实测同样连通——诊断输出 CDN OK, connected as admin@localhost/converse.js-102415756。
💡 说明:Prosody 0.12 的 MAM 对 Converse 发出的
max_items归档查询回bad-request(控制台可见一条告警噪音),不影响实时消息收发;需要历史归档时在服务端启用并按其文档配置 MAM 模块即可。CDN 引入时curve25519_compiled.wasm的 MIME 告警同理不影响功能(自托管修复法见安装篇 9.3)。
5.3 embedded 模式:嵌入页面指定区域
embedded 模式把聊天窗嵌入页面流中的指定区域而非浮窗,官方用法要点:置 view_mode: 'embedded',并为聊天窗准备挂载节点(在 Web Component 场景用 root 指向 shadow-root):
converse.initialize({
websocket_url: "wss://im.example.com/xmpp-websocket",
view_mode: "embedded",
discover_connection_methods: false,
i18n: "zh_CN"
});
宿主侧需自行用 CSS 约束挂载区域的高度(聊天窗不自带页面级布局),适合「文档页右侧讨论栏」这类形态。若只需要右下角入口,overlayed 更省事。
六、注意事项
- HTTPS 是生产硬前提:SCRAM/WebCrypto 安全上下文限制(5.1 步骤 6),页面与 WebSocket 端点都要 TLS(
wss://); - 六个踩坑汇总:ESM 加载(
type="module")、Prosody conf.d 模块覆盖(传输模块写全局段)、跨源 403(cross_domain_websocket=true)、端点发现阻断(discover_connection_methods=false)、WebCrypto 安全上下文、插件白名单(whitelisted_plugins)——均为实测复现,按 5.1/5.2 对应步骤规避; - OMEMO 端到端加密需对端客户端同样支持,且首次使用要处理设备信任;嵌入客服这类「站点对访客」场景一般不开;
- 免登集成:把聊天接入已有账号体系时用
authentication: 'prebind'(后端先与 XMPP 建立会话再交前端)或credentials_url换发凭据,避免用户二次输入密码; - 嵌入不是隔离:Converse.js 与宿主页面同域运行,宿主页面脚本可读聊天 DOM;不要把嵌入当作多租户隔离手段;
- 升级锁定版本号:自托管锁定 dist 版本、CDN 锁定 URL 版本段,跨大版本升级先核对官方 CHANGES(v13→v14 的 ESM 切换即Breaking Change)。
七、参考链接
- 官方插件开发文档(
this._converse.api/whitelisted_plugins):https://conversejs.org/docs/html/plugin_development.html - 官方配置项文档(view_mode 等):https://conversejs.org/docs/html/configuration.html
- Prosody mod_websocket:https://prosody.im/doc/websocket
- XEP-0156 连接端点发现:https://xmpp.org/extensions/xep-0156.html
- MDN 安全上下文(Secure Contexts):https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
本专栏其他篇目:《Converse.js 介绍与选型指南》、《Ubuntu 22.04 安装 Converse.js 14 指南》、《Converse.js Docker Compose 部署指南》、《Converse.js 14 配置参数详解》。