eventsource 无法传多个 topic 参数,因 mercure 规范和浏览器实现均要求 topic 为单一字符串;正确做法是通过 jwt 的 mercure.subscribe 字段声明多个主题模板,支持通配符匹配。

FrankenPHP 的 Mercure 不支持单个 EventSource 实例订阅多个 topic——这是协议限制,不是配置问题。必须为每个主题单独创建连接,或改用通配符路径在 JWT 中声明批量权限。
为什么 EventSource 不能传多个 topic 参数
Mercure 规范要求 topic 查询参数是单一字符串,浏览器 EventSource 也只接受一个 URL。即使你写成 ?topic=a&topic=b,服务端也只取第一个值,其余被忽略。这不是 FrankenPHP 的 bug,而是 HTTP+EventSource 的固有行为。
正确做法:用 mercure.subscribe JWT 字段声明多个允许主题
前端仍只开一个 EventSource,但后端签发的 subscriber JWT 必须在 mercure.subscribe 字段中列出所有合法 topic 模板:
- JWT payload 示例:
{"mercure": {"subscribe": ["https://api.example.com/notifications/me", "https://api.example.com/tasks/42/*"]}} - 前端订阅任意一个匹配的 topic 即可,比如
new EventSource('/.well-known/mercure?topic=https://api.example.com/notifications/me') - 发布时只要往其中任一 topic 推送(如
publish('https://api.example.com/tasks/42/status')),且该 URI 被mercure.subscribe中的https://api.example.com/tasks/42/*匹配,就能收到 - 注意:通配符
*只匹配一级路径段,/tasks/42/*不匹配/tasks/42/logs/error
手动管理多个 EventSource 实例的注意事项
如果业务逻辑上必须区分不同 topic 的响应(比如通知走弹窗、任务进度走进度条),那就得显式维护多个 EventSource:
- 每个实例需独立
close(),否则页面跳转后残留连接会持续占用 Hub 连接数 - 避免重复订阅同一 topic:检查
window.__es_instances或用Map缓存已创建的实例,key 为 topic 字符串 - 不要在 SPA 路由守卫里无条件新建——先
abort()前一个控制器(若用了AbortController),再建新的 - Chrome 对同域
EventSource默认最多 6 个并发连接,超限会排队;Safari 更严,可能直接拒绝
容易被忽略的路径细节:尾部斜杠影响 topic 匹配
Mercure 把 https://api.example.com/notifications 和 https://api.example.com/notifications/ 当作两个完全不同的 topic。JWT 中写的 subscribe 条目、前端传的 topic 参数、后端 publish() 的第一个参数,三者必须字面量完全一致,包括协议、域名、大小写、路径分隔符和末尾斜杠。
建议统一约定:所有 topic 使用无尾部斜杠的绝对 URI,并在生成 JWT 和调用 publish() 前做 rtrim($uri, '/') 处理。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











