跳转到内容

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 的关键设计可以拆成 三层

  1. 协议层完全外包给 matrix-js-sdk:UI 一行 Matrix 协议代码都没有。所有”房间、事件、加密、设备验证”都由 SDK 的 MatrixClient 实例托管,UI 只读它的状态。类比:UI 是仪表盘,SDK 是发动机,仪表盘换皮不影响发动机。

  2. SPA + React 状态由 sync stream 驱动:登录后 SDK 一直跑 /sync 长轮询,每来一批事件就发 EventEmitter 事件,React 组件订阅后局部重渲染。整个 UI 是事件驱动的”时间线投影”。

  3. 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 的 /send API
  • 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。

config.json
{
"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 起不到任何作用。

踩过的坑

  1. 把 Element Web 当 Matrix 协议本身:它只是参考实现之一。Cinny、FluffyChat、Hydrogen 也都是 Matrix web 客户端,背后协议规范才是 SoT。

  2. 自建时只起 Element Web 不起 homeserver:Element Web 是纯静态资源,不存任何用户数据,必须先有 synapse 或 Dendrite。新人常以为”装了 Element 就能用”。

  3. 跨设备登录后历史消息全是红盾:E2EE 房间的密钥不会自动跨设备同步。新设备必须做 cross-signing 验证 + 紧急密钥备份恢复,否则历史消息永远解不开——这是 Matrix 上手路径上最劝退的一步。

  4. 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 年走下来,是少数还在维护的”协议参考客户端”之一。

学到什么

  1. 客户端可以是协议规范的”门面 + 兜底”:Matrix 没有 Element Web,协议会变成”理论上能跑但没人见过”
  2. 瘦 UI + 厚 SDK 是协议类项目的健康分层——同一个 SDK 能喂 web、Electron、扩展
  3. 重写 ≠ 替代:Element X 走鸟群路线(Rust SDK 重写)但 Element Web 仍在维护,因为 web 端重写没那么急
  4. 品牌迁移很贵:Vector → Riot → Element 改名两次,每次都要做三端 + 应用市场 + 域名 + 文档同步

延伸阅读

关联

  • 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 长轮询)