终极指南:如何在React、Vue、Angular中集成Mercure实现实时通信

张开发
2026/4/18 17:48:36 15 分钟阅读

分享文章

终极指南:如何在React、Vue、Angular中集成Mercure实现实时通信
终极指南如何在React、Vue、Angular中集成Mercure实现实时通信【免费下载链接】mercure An open, easy, fast, reliable and battery-efficient solution for real-time communications项目地址: https://gitcode.com/gh_mirrors/me/mercure想要为你的React、Vue或Angular应用添加实时通信功能吗Mercure协议正是你需要的解决方案 Mercure是一个开源、简单、快速、可靠且电池友好的实时通信协议专门为推送数据更新到Web浏览器和其他HTTP客户端而设计。本文将为你提供完整的Mercure集成指南帮助你在现代前端框架中轻松实现实时功能。什么是Mercure实时通信协议Mercure是一个基于Server-Sent EventsSSE的发布-订阅协议它使服务器能够实时向客户端推送更新。与WebSocket相比Mercure具有更好的电池效率、更简单的实现和内置的授权机制。如上图所示Mercure的核心架构包括应用服务器、Mercure Hub和客户端设备。应用服务器通过POST请求向Mercure Hub发布数据而Mercure Hub则通过SSE协议向所有订阅了相关主题的客户端推送实时数据。为什么选择Mercure进行前端实时通信主要优势简单易用基于HTTP标准无需复杂的握手协议电池友好相比WebSocket更省电自动重连内置连接恢复机制浏览器原生支持现代浏览器都支持EventSource API权限控制内置JWT授权机制多框架兼容可在React、Vue、Angular等任何前端框架中使用核心概念主题Topic客户端订阅的资源标识符通常是URLHubMercure服务器负责接收发布的消息并分发给订阅者订阅Subscribe客户端向Hub注册接收特定主题的更新发布Publish服务器向Hub发送更新消息在React中集成Mercure安装依赖首先安装必要的依赖包npm install eventsource-polyfill创建Mercure Hook在React中我们可以创建一个自定义Hook来管理Mercure连接// hooks/useMercure.js import { useEffect, useRef, useState } from react; export function useMercure(hubUrl, topics, onMessage) { const eventSourceRef useRef(null); const [isConnected, setIsConnected] useState(false); useEffect(() { if (!hubUrl || !topics || topics.length 0) return; const url new URL(hubUrl); topics.forEach(topic { url.searchParams.append(topic, topic); }); // 创建EventSource连接 const eventSource new EventSource(url); eventSourceRef.current eventSource; eventSource.onopen () { console.log(Mercure连接已建立); setIsConnected(true); }; eventSource.onmessage (event) { try { const data JSON.parse(event.data); onMessage(data); } catch (error) { console.error(解析Mercure消息失败:, error); } }; eventSource.onerror (error) { console.error(Mercure连接错误:, error); setIsConnected(false); }; // 清理函数 return () { if (eventSourceRef.current) { eventSourceRef.current.close(); eventSourceRef.current null; setIsConnected(false); } }; }, [hubUrl, topics.join(,), onMessage]); return { isConnected }; }在组件中使用// components/RealTimeChat.jsx import React, { useState } from react; import { useMercure } from ../hooks/useMercure; function RealTimeChat() { const [messages, setMessages] useState([]); const [newMessage, setNewMessage] useState(); const hubUrl https://localhost/.well-known/mercure; const topics [https://example.com/chat/messages]; const handleNewMessage (message) { setMessages(prev [...prev, message]); }; const { isConnected } useMercure(hubUrl, topics, handleNewMessage); const sendMessage async () { const response await fetch(hubUrl, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded, Authorization: Bearer YOUR_JWT_TOKEN }, body: new URLSearchParams({ topic: https://example.com/chat/messages, data: JSON.stringify({ text: newMessage, timestamp: new Date().toISOString(), user: current-user }) }) }); if (response.ok) { setNewMessage(); } }; return ( div classNamechat-container div classNameconnection-status 连接状态: {isConnected ? 已连接 : 断开} /div div classNamemessages {messages.map((msg, index) ( div key{index} classNamemessage strong{msg.user}:/strong {msg.text} /div ))} /div div classNamemessage-input input typetext value{newMessage} onChange{(e) setNewMessage(e.target.value)} placeholder输入消息... / button onClick{sendMessage} disabled{!isConnected} 发送 /button /div /div ); }在Vue中集成Mercure安装依赖npm install eventsource创建Mercure插件// plugins/mercure.js import EventSource from eventsource; export default { install(app, options) { const mercure { connections: new Map(), connect(hubUrl, topics, callback) { const url new URL(hubUrl); topics.forEach(topic { url.searchParams.append(topic, topic); }); const eventSource new EventSource(url.toString()); eventSource.onmessage (event) { try { const data JSON.parse(event.data); callback(data); } catch (error) { console.error(解析Mercure消息失败:, error); } }; eventSource.onerror (error) { console.error(Mercure连接错误:, error); }; const connectionId Date.now().toString(); this.connections.set(connectionId, eventSource); return connectionId; }, disconnect(connectionId) { const eventSource this.connections.get(connectionId); if (eventSource) { eventSource.close(); this.connections.delete(connectionId); } }, publish(hubUrl, topic, data, jwtToken) { return fetch(hubUrl, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded, Authorization: Bearer ${jwtToken} }, body: new URLSearchParams({ topic, data: JSON.stringify(data) }) }); } }; app.config.globalProperties.$mercure mercure; app.provide(mercure, mercure); } };在Vue组件中使用!-- components/RealTimeNotifications.vue -- template div classnotifications div classstatus :class{ connected: isConnected } {{ isConnected ? 实时通知已启用 : 连接断开 }} /div div classnotification-list div v-for(notification, index) in notifications :keyindex classnotification :classnotification.type div classnotification-title{{ notification.title }}/div div classnotification-content{{ notification.content }}/div div classnotification-time{{ formatTime(notification.timestamp) }}/div /div /div /div /template script import { ref, onMounted, onUnmounted } from vue; export default { name: RealTimeNotifications, setup() { const notifications ref([]); const isConnected ref(false); let connectionId null; onMounted(() { // 连接到Mercure Hub connectionId inject(mercure).connect( https://localhost/.well-known/mercure, [https://example.com/notifications], (data) { notifications.value.unshift(data); // 限制通知数量 if (notifications.value.length 50) { notifications.value notifications.value.slice(0, 50); } } ); isConnected.value true; }); onUnmounted(() { if (connectionId) { inject(mercure).disconnect(connectionId); } }); const formatTime (timestamp) { return new Date(timestamp).toLocaleTimeString(); }; return { notifications, isConnected, formatTime }; } }; /script style scoped .notifications { border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; max-height: 400px; overflow-y: auto; } .status { padding: 8px; border-radius: 4px; margin-bottom: 16px; text-align: center; font-weight: bold; } .status.connected { background-color: #d4edda; color: #155724; } .notification { padding: 12px; margin-bottom: 8px; border-left: 4px solid #007bff; background-color: #f8f9fa; border-radius: 4px; } .notification.info { border-left-color: #17a2b8; } .notification.warning { border-left-color: #ffc107; } .notification.error { border-left-color: #dc3545; } .notification-title { font-weight: bold; margin-bottom: 4px; } .notification-content { color: #666; margin-bottom: 4px; } .notification-time { font-size: 12px; color: #999; text-align: right; } /style在Angular中集成Mercure安装依赖npm install eventsource创建Mercure服务// services/mercure.service.ts import { Injectable, OnDestroy } from angular/core; import { Observable, Subject } from rxjs; Injectable({ providedIn: root }) export class MercureService implements OnDestroy { private eventSource: EventSource | null null; private messageSubject new Subjectany(); private connectionStatusSubject new Subjectboolean(); public messages$ this.messageSubject.asObservable(); public connectionStatus$ this.connectionStatusSubject.asObservable(); connect(hubUrl: string, topics: string[]): void { if (this.eventSource) { this.disconnect(); } const url new URL(hubUrl); topics.forEach(topic { url.searchParams.append(topic, topic); }); this.eventSource new EventSource(url.toString()); this.eventSource.onopen () { console.log(Mercure连接已建立); this.connectionStatusSubject.next(true); }; this.eventSource.onmessage (event) { try { const data JSON.parse(event.data); this.messageSubject.next(data); } catch (error) { console.error(解析Mercure消息失败:, error); } }; this.eventSource.onerror (error) { console.error(Mercure连接错误:, error); this.connectionStatusSubject.next(false); }; } disconnect(): void { if (this.eventSource) { this.eventSource.close(); this.eventSource null; this.connectionStatusSubject.next(false); } } async publish(hubUrl: string, topic: string, data: any, jwtToken: string): PromiseResponse { return fetch(hubUrl, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded, Authorization: Bearer ${jwtToken} }, body: new URLSearchParams({ topic, data: JSON.stringify(data) }) }); } ngOnDestroy(): void { this.disconnect(); this.messageSubject.complete(); this.connectionStatusSubject.complete(); } }创建实时仪表板组件// components/real-time-dashboard/real-time-dashboard.component.ts import { Component, OnInit, OnDestroy } from angular/core; import { MercureService } from ../../services/mercure.service; import { Subscription } from rxjs; interface DashboardMetric { name: string; value: number; unit: string; trend: up | down | stable; timestamp: Date; } Component({ selector: app-real-time-dashboard, templateUrl: ./real-time-dashboard.component.html, styleUrls: [./real-time-dashboard.component.scss] }) export class RealTimeDashboardComponent implements OnInit, OnDestroy { metrics: DashboardMetric[] []; isConnected false; private subscriptions: Subscription[] []; constructor(private mercureService: MercureService) {} ngOnInit(): void { // 订阅连接状态 this.subscriptions.push( this.mercureService.connectionStatus$.subscribe(status { this.isConnected status; }) ); // 订阅实时数据 this.subscriptions.push( this.mercureService.messages$.subscribe(data { this.updateMetrics(data); }) ); // 连接到Mercure Hub this.mercureService.connect( https://localhost/.well-known/mercure, [ https://example.com/metrics/cpu, https://example.com/metrics/memory, https://example.com/metrics/network ] ); } updateMetrics(data: any): void { const existingIndex this.metrics.findIndex(m m.name data.name); const metric: DashboardMetric { name: data.name, value: data.value, unit: data.unit || , trend: this.calculateTrend(data.value, data.previousValue), timestamp: new Date() }; if (existingIndex 0) { this.metrics[existingIndex] metric; } else { this.metrics.push(metric); } } calculateTrend(current: number, previous?: number): up | down | stable { if (!previous) return stable; if (current previous) return up; if (current previous) return down; return stable; } ngOnDestroy(): void { this.subscriptions.forEach(sub sub.unsubscribe()); this.mercureService.disconnect(); } }!-- components/real-time-dashboard/real-time-dashboard.component.html -- div classdashboard div classdashboard-header h2实时监控仪表板/h2 div classconnection-status [class.connected]isConnected {{ isConnected ? 实时数据已连接 : 数据源断开 }} /div /div div classmetrics-grid div *ngForlet metric of metrics classmetric-card div classmetric-header h3{{ metric.name }}/h3 span classtrend-indicator [class]metric.trend {{ metric.trend up ? : metric.trend down ? : ➡️ }} /span /div div classmetric-value span classvalue{{ metric.value }}/span span classunit{{ metric.unit }}/span /div div classmetric-footer small最后更新: {{ metric.timestamp | date:HH:mm:ss }}/small /div /div /div /divMercure授权机制详解Mercure使用JWTJSON Web Token进行授权控制如上图所示。客户端从应用服务器获取包含权限信息的JWT令牌该令牌定义了用户可以订阅和发布的主题范围。JWT配置示例// 生成JWT令牌 const jwt require(jsonwebtoken); const jwtKey your-secret-key; const token jwt.sign( { mercure: { publish: [*], // 允许发布所有主题 subscribe: [https://example.com/chat/*] // 只允许订阅聊天相关主题 } }, jwtKey, { algorithm: HS256 } );Mercure发现机制Mercure支持自动发现机制客户端可以通过HTTP Link头自动发现Hub的URL简化了配置过程。自动发现实现async function discoverMercureHub(resourceUrl) { const response await fetch(resourceUrl); const linkHeader response.headers.get(Link); if (linkHeader) { const match linkHeader.match(/([^]);\srel(?:mercure|[^]*mercure[^]*)/); if (match) { return match[1]; // 返回Hub URL } } return null; // 未找到Hub }最佳实践和性能优化1. 连接管理在组件卸载时关闭连接实现自动重连机制使用心跳保持连接活跃2. 错误处理// 增强的错误处理 eventSource.onerror (error) { console.error(Mercure连接错误:, error); // 实现指数退避重连 setTimeout(() { if (!eventSource || eventSource.readyState EventSource.CLOSED) { reconnect(); } }, Math.min(1000 * Math.pow(2, retryCount), 30000)); };3. 性能优化批量处理消息更新使用虚拟滚动处理大量消息实现消息去重机制使用主题过滤减少不必要的数据传输部署和配置Docker部署docker run -d \ -p 3000:80 \ -e JWT_KEYyour-secret-key \ -e ALLOW_ANONYMOUS1 \ dunglas/mercure配置示例# mercure.yaml 配置文件 addr: :3000 jwt_key: your-secret-key allow_anonymous: true cors_allowed_origins: - http://localhost:3000 - https://your-app.com publish_allowed_origins: - http://localhost:3000总结Mercure为React、Vue和Angular应用提供了简单、高效的实时通信解决方案。通过本文的指南你可以快速集成在三大主流前端框架中轻松集成Mercure实现实时功能构建聊天、通知、仪表板等实时应用保障安全性利用JWT进行细粒度的权限控制优化性能遵循最佳实践确保应用性能无论你是构建社交应用、实时监控系统还是协作工具Mercure都能为你提供稳定可靠的实时通信能力。现在就开始在你的项目中集成Mercure享受简单高效的实时通信体验吧官方文档docs/getting-started.md 示例代码examples/chat/static/chat.js 核心实现hub.go【免费下载链接】mercure An open, easy, fast, reliable and battery-efficient solution for real-time communications项目地址: https://gitcode.com/gh_mirrors/me/mercure创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

更多文章