基于Vue+WebSocket實現(xiàn)實時數(shù)據(jù)可視化的實戰(zhàn)指南
一、項目背景與核心需求
- 實時接收多個 WebSocket 數(shù)據(jù)源(不同服務(wù)器或端口)
- 設(shè)計模擬數(shù)據(jù)接口,方便本地開發(fā)調(diào)試
- 支持數(shù)據(jù)的自動合并(如車流總量、車輛類型分布)
- 使用 ECharts 動態(tài)展示統(tǒng)計數(shù)據(jù)
- 保證 WebSocket 斷線自動重連,提高穩(wěn)定性
二、項目架構(gòu)與核心狀態(tài)管理
定義一個全局響應(yīng)式對象 sources
,分別存儲四個數(shù)據(jù)源的數(shù)據(jù),支持真實數(shù)據(jù)和模擬數(shù)據(jù)統(tǒng)一寫入。
const USE_MOCK = true; // 是否啟用模擬數(shù)據(jù) const sources = reactive({ su1: {}, su2: {}, su3: {}, su4: {}, });
三、WebSocket 連接與模擬數(shù)據(jù)設(shè)計
1. 真實 WebSocket 連接實現(xiàn)
使用原生 WebSocket 連接服務(wù)器,設(shè)置事件監(jiān)聽,支持斷線自動重連。
function createRealWS(url, setTarget) { const ws = new WebSocket(url); ws.onopen = () => console.log(`?? WebSocket ${url} 已連接`); ws.onmessage = (event) => { const data = JSON.parse(event.data); setTarget(JSON.parse(data.data)); }; ws.onerror = () => console.error(`WebSocket ${url} 出錯`); ws.onclose = () => { console.warn(`WebSocket ${url} 關(guān)閉,3秒后重連...`); setTimeout(() => createRealWS(url, setTarget), 3000); }; }
2. 模擬數(shù)據(jù)接口
為了方便本地開發(fā),使用定時器生成結(jié)構(gòu)一致的模擬數(shù)據(jù),模擬數(shù)據(jù)每3秒刷新一次。
function createMockSource(setTarget) { setInterval(() => { const mock = { timestamp: Date.now(), globalTime: new Date().toLocaleString(), totalVehiCount: Math.floor(Math.random() * 1000), aveSpeed: +(30 + Math.random() * 10).toFixed(2), numVehiByType: Object.fromEntries( [1, 2, 3, 7, 8, 10, 11, 15, 100].map(k => [k, Math.floor(Math.random() * 100)]) ), }; setTarget(mock); }, 3000); }
3. 初始化所有數(shù)據(jù)源
根據(jù) WebSocket URL 端口號映射到對應(yīng)數(shù)據(jù)源,啟用模擬或真實數(shù)據(jù)。
function initAllSources() { const urls = [ "ws://xx/wsStatisJd", "ws://xx/wsStatisJd", "ws://xx/wsStatisJd", "ws://xx/wsStatisJd", ]; urls.forEach((url) => { const key = getSourceKeyByPort(url); // su1 su2 su3 su4 const setFn = (data) => (sources[key] = data); if (USE_MOCK) { createMockSource(setFn); } else { createRealWS(url, setFn); } }); }
四、數(shù)據(jù)合并與格式化
合并車流總量
將四個數(shù)據(jù)源的車輛總數(shù)相加,確保數(shù)值準確。
function mergeTotal(...totals) { return totals.reduce((sum, val) => sum + Number(val || 0), 0); }
合并車輛類型分布
對每種車輛類型進行累加。
格式化合并后的車輛類型數(shù)據(jù),固定順序輸出并計算百分比
function formatVehicleTypeData(numVehiByType, total = 0) { const fixedOrder = [8, 3, 2, 1, 15, 7, 10, 11, 100]; return fixedOrder.map(key => { const value = numVehiByType[key] || 0; const name = vehicleTypeMap[key] || `類型${key}`; const percent = total > 0 ? ((value / total) * 100).toFixed(1) : "0.0"; return { name, value, percent: Number(percent), color: vehicleColorMap[name] || "#999999", }; }); }
五、響應(yīng)式數(shù)據(jù)更新與圖表刷新
通過 watchEffect
監(jiān)聽數(shù)據(jù)變化,自動計算合并數(shù)據(jù)并刷新 ECharts 餅圖。
watchEffect(() => { const { su1, su2, su3, su4 } = sources; const mergedTotal = mergeTotal( su1.totalVehiCount, su2.totalVehiCount, su3.totalVehiCount, su4.totalVehiCount ); const mergedType = mergeVehicleType( su1.numVehiByType || {}, su2.numVehiByType || {}, su3.numVehiByType || {}, su4.numVehiByType || {} ); chartData.value = formatVehicleTypeData(mergedType, mergedTotal); realtimeTime.value = new Date().toLocaleString(); totalVehiCount.value = mergedTotal; aveSpeed.value = { su1: su1.aveSpeed || 0, su2: su2.aveSpeed || 0, su3: su3.aveSpeed || 0, su4: su4.aveSpeed || 0, }; InitEchart2(chartData.value); });
六、ECharts 餅圖動態(tài)渲染
初始化并動態(tài)更新餅圖,顏色對應(yīng)車輛類型,關(guān)閉標簽和提示框保證大屏美觀。
const InitEchart2 = (data) => { const chartDom = document.getElementById("map-left-4-1-echarts"); if (!chartDom) return; if (!myChart) { myChart = echarts.init(chartDom); } const colorList = data.map(item => item.color || "#ccc"); myChart.setOption({ color: colorList, tooltip: { show: false }, series: [{ name: "車輛類型占比", type: "pie", radius: ["55%", "80%"], avoidLabelOverlap: false, itemStyle: { borderRadius: 1, borderColor: "#2c3950", borderWidth: 2, }, label: { show: false }, emphasis: { scale: false, label: { show: false } }, labelLine: { show: false }, data: data.map(({ name, value }) => ({ name, value })), }], }); };
七、啟動與定時刷新邏輯
在組件掛載時,初始化數(shù)據(jù)接口和數(shù)據(jù)源,并設(shè)置定時器周期刷新相關(guān)統(tǒng)計數(shù)據(jù)。
onMounted(() => { curDayCountData(); // 獲取今日車流初始數(shù)據(jù) initAllSources(); // 啟動 WebSocket / 模擬數(shù)據(jù) getDeviceOnlineData(); // 設(shè)備在線率數(shù)據(jù) curDayEventCountData(); // 今日事件統(tǒng)計 eventHistoryCountData(); // 事件歷史統(tǒng)計 dataRefreshTimer = setInterval(() => { curDayCountData(); getDeviceOnlineData(); curDayEventCountData(); eventHistoryCountData(); }, 30000); }); onUnmounted(() => { if (dataRefreshTimer) clearInterval(dataRefreshTimer); });
八、總結(jié)
- 多數(shù)據(jù)源實時管理,靈活切換模擬/真實數(shù)據(jù),提升開發(fā)效率
- 自動重連機制,保證 WebSocket 長連接穩(wěn)定可靠
- 響應(yīng)式合并處理,統(tǒng)一計算統(tǒng)計數(shù)據(jù),確保展示準確
- ECharts 動態(tài)刷新,實現(xiàn)流暢視覺效果,符合大屏需求
如果你正在做交通、工業(yè)或監(jiān)控領(lǐng)域的實時可視化,這個方案值得借鑒。
以上就是基于Vue+WebSocket實現(xiàn)實時數(shù)據(jù)可視化的實戰(zhàn)指南的詳細內(nèi)容,更多關(guān)于Vue WebSocket實時數(shù)據(jù)可視化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue實現(xiàn)todolist功能、todolist組件拆分及todolist的刪除功能
這篇文章主要介紹了vue實現(xiàn)todolist功能、todolist組件拆分及todolist的刪除功能,需要的朋友可以參考下2019-04-04vue+vant使用圖片預(yù)覽功能ImagePreview的問題解決
這篇文章主要介紹了vue+vant使用圖片預(yù)覽功能ImagePreview的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04Vue項目pdf(base64)轉(zhuǎn)圖片遇到的問題及解決方法
這篇文章主要介紹了Vue項目pdf(base64)轉(zhuǎn)圖片遇到的問題及解決方法,需要的朋友可以參考下2018-10-10vue如何實現(xiàn)列表自動滾動、向上滾動的效果(vue-seamless-scroll)
這篇文章主要介紹了vue如何實現(xiàn)列表自動滾動、向上滾動的效果(vue-seamless-scroll),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05