Element Web — Matrix 协议旗舰 web 客户端(React + matrix-js-sdk)
是什么
Element Web 是 Matrix 协议的旗舰网页客户端,前身是 Riot.im(再前身叫 Vector),由 Element 公司维护。日常类比:你可以把它想成”Matrix 协议的官方门面 App”——就像微信网页版之于微信协议,Slack web 之于 Slack 后端,但跟那两个不同的是,背后的协议是开放的、服务器是任何人都能架的。
Element Web 是一个 TypeScript + React 单页应用:浏览器打开就能用,不装客户端。所有协议层的事——登录、同步房间、收发消息、端到端加密——都不在 UI 里写,而是甩给底层的 matrix-js-sdk 处理。React 组件只负责把 SDK 暴露的状态画成时间线、消息气泡、设置面板。
最小用法:浏览器打开 https://app.element.io,用 matrix.org 账号登录,就能加房间发消息——这套代码同时也是 element-desktop 的内核(Electron 把它打包成桌面 app)。
// MatrixChat 顶层组件大致长这样(src/components/structures/MatrixChat.tsx 节选示意)const client = createClient({ baseUrl, accessToken, userId })client.startClient({ initialSyncLimit: 20 }) // 启动 sync 长轮询client.on("Room.timeline", (event) => { // SDK 把新事件甩上来,UI 重渲染时间线})注意:移动端的 element-x-android / element-x-ios 是另起的原生项目,不共享代码,只共享协议规范。
为什么重要
不理解 Element Web,下面这些事都没法解释:
- 为什么 Slack/Discord 是中心化的、Matrix 不是——后者每个 homeserver 各自存数据,Element Web 只是那个能连任意服务器的”标准浏览器壳”
- 为什么”Matrix 协议”和”Element 这家公司”不是一回事——协议是开放规范,Element Web 只是其中一个客户端实现
- 为什么自建 IM 时只起 synapse 而没有 UI 是不够的——homeserver 只暴露 API,普通用户看不见,需要 Element Web 这层壳
- 为什么 Element Web、element-desktop、element-android 三个项目”看着像但又不是同一份代码”
核心要点
Element Web 的关键设计可以拆成 三层:
-
协议层完全外包给 matrix-js-sdk:UI 一行 Matrix 协议代码都没有。所有”房间、事件、加密、设备验证”都由 SDK 的
MatrixClient实例托管,UI 只读它的状态。类比:UI 是仪表盘,SDK 是发动机,仪表盘换皮不影响发动机。 -
SPA + React 状态由 sync stream 驱动:登录后 SDK 一直跑
/sync长轮询,每来一批事件就发 EventEmitter 事件,React 组件订阅后局部重渲染。整个 UI 是事件驱动的”时间线投影”。 -
E2EE 在 SDK 里黑盒完成:UI 看到的永远是明文。早期用 matrix-rust-sdk 的祖师爷 libolm(C 实现),2024 年起逐步迁到 vodozemac(Rust 实现,bug 一次修三端)。设备验证、跨设备签名、密钥备份都在 SDK 层。
加起来,这是一份”瘦壳 + 厚 SDK”架构的典型案例。
实践案例
案例 1:用 app.element.io 跑通最小闭环
不装任何东西,浏览器打开 https://app.element.io,用 matrix.org 公网账号登录,加入 #matrix:matrix.org 房间发一条消息。这一条消息背后:
- 浏览器加载 element.io 上静态托管的 Element Web(一堆 JS/CSS bundle)
- Element Web 通过 matrix-js-sdk 调 matrix.org homeserver 的
/sendAPI - homeserver 把事件存进房间 DAG,并联邦推到房间里其他 homeserver
- 其他 homeserver 上的客户端从自己的
/sync拿到这条事件,UI 渲染
整条链路你只用了一个浏览器 tab,但触到了 Matrix 协议的全部要素:客户端、homeserver、联邦。
案例 2:自建私有 IM 替代 Slack
部署清单:一台 VPS + 域名 → 装 Synapse 做 homeserver → nginx 反代 + .well-known 文件指向 homeserver → 静态托管 Element Web 到另一个域名 → 配置 config.json 让 Element Web 默认连你的 homeserver。
{ "default_server_config": { "m.homeserver": { "base_url": "https://matrix.example.com" } }, "disable_guests": true}之后团队成员浏览器开网页就能用,消息全在自己服务器。和 Slack 比,省的是订阅费 + 数据自主,付的是运维。
案例 3:读源码搞懂 Matrix sync 怎么驱动 UI
入口 src/components/structures/MatrixChat.tsx 是顶层 stateful 组件,它持有 MatrixClient 实例,启动后所有子组件通过 React Context 拿到 client。
时间线组件 TimelinePanel.tsx 监听 Room.timeline 事件,每个事件 push 进本地数组触发重渲染:
client.on("Room.timeline", (event, room) => { if (room.roomId === currentRoomId) { setEvents((prev) => [...prev, event]) }})读这条链能搞懂”为什么这是 SPA 而不是 SSR”——因为它本质是一台长跑的客户端,刷新一次浏览器就要重新 sync 几秒,SSR 起不到任何作用。
踩过的坑
-
把 Element Web 当 Matrix 协议本身:它只是参考实现之一。Cinny、FluffyChat、Hydrogen 也都是 Matrix web 客户端,背后协议规范才是 SoT。
-
自建时只起 Element Web 不起 homeserver:Element Web 是纯静态资源,不存任何用户数据,必须先有 synapse 或 Dendrite。新人常以为”装了 Element 就能用”。
-
跨设备登录后历史消息全是红盾:E2EE 房间的密钥不会自动跨设备同步。新设备必须做 cross-signing 验证 + 紧急密钥备份恢复,否则历史消息永远解不开——这是 Matrix 上手路径上最劝退的一步。
-
Element Web ≠ Element X:Element X 是 2023 年起的下一代客户端(基于 matrix-rust-sdk 重写),目前只有 mobile,web 端 Element X 还在内测。看仓库时别张冠李戴。
适用 vs 不适用场景
适用:
- 想体验 Matrix 协议、不想装 app(直接 app.element.io)
- 自建团队 IM、需要”开网页就能用”的轻量入口
- 学 Matrix 协议时拿来当参考实现读源码
- 跟 Synapse/Dendrite 配套部署做去中心化通讯
不适用:
- 移动端日常使用(用原生 element-x-android / element-x-ios,更省电、加密更稳)
- 嵌入到自家产品里(用 matrix-js-sdk 直接搭轻量 UI 更合适,Element Web 体量大)
- 想要的不是 Matrix 而是别的协议(XMPP / IRC / Signal 协议)—— Element Web 只懂 Matrix
历史小故事(可跳过)
- 2014 年:Matrix 协议在 Amdocs 内部启动,目标做”任何 IM 之间能互通的 SMS 替代物”。
- 2016 年:Vector.im 上线,作为协议参考客户端。
- 2018 年:因为名字像金融词,改名 Riot.im。
- 2020 年:母公司 New Vector 改名 Element,Riot.im 改名 Element,三端统一品牌。
- 2024 年:仓库从
vector-im/element-web迁到element-hq/element-web,许可证从 Apache 2.0 改成 AGPL-3.0,同步推动重心向 Element X 系列倾斜。
10 年走下来,是少数还在维护的”协议参考客户端”之一。
学到什么
- 客户端可以是协议规范的”门面 + 兜底”:Matrix 没有 Element Web,协议会变成”理论上能跑但没人见过”
- 瘦 UI + 厚 SDK 是协议类项目的健康分层——同一个 SDK 能喂 web、Electron、扩展
- 重写 ≠ 替代:Element X 走鸟群路线(Rust SDK 重写)但 Element Web 仍在维护,因为 web 端重写没那么急
- 品牌迁移很贵:Vector → Riot → Element 改名两次,每次都要做三端 + 应用市场 + 域名 + 文档同步
延伸阅读
- 官方文档:Element 部署指南(自建必读)
- Matrix 协议规范:matrix.org/docs/spec(协议层 SoT)
- 视频:Matrix.org — How E2EE Works in Element(看密钥怎么协商)
- matrix-js-sdk —— Element Web 的协议层心脏
- synapse —— Element Web 最常配的 homeserver
关联
- matrix-js-sdk —— Element Web 一切协议能力的底层 SDK,UI 是它的展示器
- synapse —— Element Web 默认连的官方 Matrix homeserver 实现
- element-android —— 同一协议的 Android 客户端,原生 Kotlin 写,不共享代码
- matrix-rust-sdk —— Element X 系列用的下一代 SDK,未来可能渗回 web 端
- react —— Element Web 的 UI 框架,状态驱动渲染时间线的引擎
反向链接
- conversations —— Conversations — Android 上把 XMPP 加上 OMEMO 端到端加密的客户端
- element-android —— Element Android — Matrix 协议官方 Android 客户端(Kotlin + Realm)
- matrix-js-sdk —— matrix-js-sdk — Matrix Web/Node 端的”老大哥”客户端 SDK
- matrix-rust-sdk —— matrix-rust-sdk — Matrix 客户端的”共享发动机”
- mattermost —— Mattermost — Slack 的开源自托管替代(Go 服务端 + React 客户端)
- prosody —— Prosody — Lua 写的轻量 XMPP 服务器(嵌入式部署 + 模块化插件)
- react —— React UI 组件库
- rocket-chat —— Rocket.Chat — 开源 Slack 替代,Meteor + MongoDB 全栈实时聊天
- signal-android —— Signal Android — 让 Android 上的每条消息都只有两端能看见
- signal-ios —— Signal iOS — 让 iPhone 上的每条消息都只有两端能看见
- signal-server —— Signal-Server — 服务端看不到任何明文的即时通信后端
- synapse —— Synapse — Matrix 协议的参考 homeserver,让聊天像电邮一样能跨服务器互通
- vodozemac —— vodozemac — Matrix 端到端加密的 Rust 内核
- zulip —— Zulip — 强制 topic 的开源团队聊天(Django + Tornado 长轮询)