如何在LightningChart JS React项目中实现单WebSocket连接下的多图表实时数据流分离
如何在LightningChart JS React项目中实现单WebSocket连接下的多图表实时数据流分离
这个场景我之前碰过好多次——一开始单图表用单WS连接没问题,图表多了之后不仅客户端资源吃紧,服务器那边的连接数压力也上来了,确实得改成单连接分发的模式。用React的上下文(Context)来统一管理WebSocket是最适合这种轻量场景的方案,不用引入复杂的状态管理库,代码也清晰。下面一步步给你讲怎么实现:
1. 先做一个全局的WebSocket上下文提供者
这个组件负责维护唯一的WebSocket连接,接收服务器的所有数据,然后把数据分发给订阅的图表组件。我们用useRef来存连接实例和订阅者列表,避免组件重渲染时重复创建:
import { createContext, useContext, useEffect, useRef, useState } from 'react'; // 定义上下文类型,提供订阅数据的方法 type WSContextType = { subscribe: (chartKey: string, callback: (data: any) => void) => () => void; }; const WSContext = createContext<WSContextType | undefined>(undefined); export function WebSocketProvider({ children }: { children: React.ReactNode }) { const wsRef = useRef<WebSocket | null>(null); // 用Map存储每个图表key对应的回调函数集合 const subscribersRef = useRef<Map<string, Set<(data: any) => void>>>(new Map()); useEffect(() => { // 建立单一WebSocket连接 const ws = new WebSocket(wsUrl); wsRef.current = ws; // 处理服务器消息 ws.onmessage = (event) => { const receivedData = JSON.parse(event.data); // 这里假设后端返回的数据结构包含chartKey字段,用来标识属于哪个图表 // 如果后端返回的是批量多图表数据(比如数组),就遍历分发 if (Array.isArray(receivedData)) { receivedData.forEach(item => { const targetCallbacks = subscribersRef.current.get(item.chartKey); targetCallbacks?.forEach(callback => callback(item.data)); }); } else { const targetCallbacks = subscribersRef.current.get(receivedData.chartKey); targetCallbacks?.forEach(callback => callback(receivedData.data)); } }; // 可选:处理连接错误和关闭 ws.onerror = (err) => console.error('WS连接错误:', err); ws.onclose = () => console.log('WS连接已关闭'); // 清理函数:组件卸载时关闭连接,清空订阅者 return () => { ws.close(); subscribersRef.current.clear(); }; }, []); // 订阅方法:每个图表组件调用这个方法订阅对应key的数据 const subscribe = (chartKey: string, callback: (data: any) => void) => { // 如果该key还没有订阅集合,就新建一个 if (!subscribersRef.current.has(chartKey)) { subscribersRef.current.set(chartKey, new Set()); } subscribersRef.current.get(chartKey)!.add(callback); // 返回取消订阅的函数,供组件卸载时调用 return () => { const callbacks = subscribersRef.current.get(chartKey); if (callbacks) { callbacks.delete(callback); // 如果该key没有订阅者了,就从Map中移除,节省内存 if (callbacks.size === 0) { subscribersRef.current.delete(chartKey); } } }; }; return ( <WSContext.Provider value={{ subscribe }}> {children} </WSContext.Provider> ); } // 自定义Hook,方便组件获取上下文 export function useWS() { const context = useContext(WSContext); if (!context) { throw new Error('useWS必须在WebSocketProvider内部使用'); } return context; }
2. 改造你的Chart组件,让它订阅数据而不是自己开连接
现在每个Chart组件只需要负责创建图表、订阅对应key的数据,不用再管理WebSocket了,代码会简洁很多:
import { useId, useContext, useEffect, useRef } from 'react'; import { useWS } from './WebSocketProvider'; import { LCContext } from './your-lc-context-path'; // 你的LightningChart上下文 type ChartProps = { chartKey: string; // 每个图表的唯一标识,用来匹配数据 }; export function Chart({ chartKey }: ChartProps) { const id = useId(); const lc = useContext(LCContext); const { subscribe } = useWS(); const lineSeriesRef = useRef<any>(null); // 存线系列,方便在订阅回调中使用 useEffect(() => { const container = document.getElementById(id) as HTMLDivElement; if (!container || !lc) return; // 创建LightningChart图表和线系列 const chart = lc.ChartXY({ container }); const lineSeries = chart .addLineSeries({ schema: { x: { pattern: "progressive" }, y: { pattern: null }, }, }) .setMaxSampleCount(100_000); lineSeriesRef.current = lineSeries; // 订阅当前chartKey的数据 const unsubscribe = subscribe(chartKey, (data) => { lineSeries.appendJSON(data); }); // 清理函数:销毁图表,取消订阅 return () => { chart.dispose(); unsubscribe(); }; }, [id, lc, chartKey, subscribe]); return <div id={id} className="chart"></div>; }
3. 在根组件中使用WebSocketProvider,渲染多个图表
最后在App或者根组件里把所有Chart包在WebSocketProvider里,每个Chart传入唯一的chartKey即可:
import { WebSocketProvider } from './WebSocketProvider'; import { Chart } from './Chart'; function App() { return ( <WebSocketProvider> <div className="dashboard-grid"> <Chart chartKey="temperature" /> <Chart chartKey="humidity" /> <Chart chartKey="pressure" /> <Chart chartKey="windSpeed" /> {/* 最多可以加10个甚至更多图表 */} </div> </WebSocketProvider> ); }
一些关键注意事项
- 后端数据格式配合:确保后端返回的每条数据都带有
chartKey(或者你自定义的标识字段),这样前端才能准确把数据分发给对应图表。如果后端返回的是全量批量数据,也可以在Provider里先按chartKey分组再分发。 - 内存泄漏防护:用
useRef存订阅者集合,组件卸载时调用unsubscribe移除回调,同时销毁LightningChart实例,避免内存泄漏。 - 可选优化:重连逻辑:如果需要高可靠性,可以在Provider里添加WS重连逻辑(比如连接断开后隔几秒自动重试),这个逻辑只需要写一次,所有图表都能受益。
- 性能优势:单一WS连接相比10个连接,能大幅减少客户端的网络资源占用,服务器端也不用维护多个连接,整体延迟和带宽消耗都会降低。LightningChart JS本身对大数据流的处理效率很高,分发数据的额外开销几乎可以忽略。
备注:内容来源于stack exchange,提问作者codegecko66
相关产品推荐
相关产品推荐

