大家在做過「告警管理」或「情報運營大屏」這類功能時,一定有過這種困惑:為什麼頁面上的數據總是慢半拍?點了查詢才刷新,不點就停在那裡。這背後其實是 HTTP 協議本身的特性——它是「請求—回應」模式,客戶端不喊話,服務端就不會主動說話。當你需要即時推送(例如新告警彈出、3D 地球熱點閃爍),傳統輪詢既浪費頻寬又延遲明顯。這篇文章要帶你認識 WebSocket:一條「長連接」的雙向通道,讓服務端能主動把數據推到前端。
一、WebSocket 與 HTTP 輪詢的差異
想像 HTTP 輪詢像你每 5 秒打一次電話問「有新消息嗎?」;而 WebSocket 是通一次電話後兩人一直線上,誰有消息隨口就說。兩者關鍵區別:
- 握手階段:WebSocket 先透過一次 HTTP 升級(Upgrade)握手,之後協議切換,連接保持打開。
- 雙向性:握手後客戶端與服務端都能主動發送訊息,不再是單向請求。
- 開銷:每條訊息沒有重複的 HTTP 頭,適合高頻小數據(即時行情、告警流)。
重點:WebSocket 並非要取代 HTTP,而是補足「即時推送」場景。靜態資源、表單提交仍用 REST 即可。
二、步驟 1:建立最小 WebSocket 服務端(Node.js)
以 ws 庫為例,幾行就能起一個回聲+主動推送的服務:
// server.js
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
console.log('客戶端已連線');
// 服務端主動每 3 秒推送一筆行情
const timer = setInterval(() => {
ws.send(JSON.stringify({
type: 'tick',
price: (Math.random() * 100).toFixed(2),
ts: Date.now(),
}));
}, 3000);
ws.on('message', (msg) => {
console.log('收到客戶端訊息:', msg.toString());
});
ws.on('close', () => clearInterval(timer));
});
console.log('WebSocket 服務監聽在 ws://localhost:8080');
三、步驟 2:前端用瀏覽器原生 API 連接
瀏覽器內建 WebSocket 物件,無須額外套件:
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
console.log('連線建立');
ws.send(JSON.stringify({ type: 'subscribe', symbol: 'FUTU' }));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到推送:', data);
// 在這裡更新 Vue 的響應式狀態或 Pinia store
};
ws.onerror = (err) => console.error('連線錯誤', err);
ws.onclose = () => console.log('連線關閉');
四、步驟 3:處理斷線重連與心跳
生產環境最容易被忽略的就是「連線會掉」。要加上重連與心跳保活:
function connect() {
const ws = new WebSocket('ws://localhost:8080');
let heartbeat;
ws.onopen = () => {
heartbeat = setInterval(() => ws.send('ping'), 15000); // 心跳
};
ws.onclose = () => {
clearInterval(heartbeat);
setTimeout(connect, 3000); // 3 秒後重連
};
return ws;
}
connect();
重點:心跳間隔要小於代理/負載均衡的空閒超時(常見 60 秒),否則連線會被中間層悄悄切斷。
五、步驟 4:在 Vue 3 中封裝成可複用 composable
把連線邏輯收進 useWebSocket,元件只管拿數據:
// useWebSocket.ts
import { ref, onUnmounted } from 'vue';
export function useWebSocket(url: string) {
const lastMessage = ref<any>(null);
const ready = ref(false);
let ws: WebSocket;
function connect() {
ws = new WebSocket(url);
ws.onopen = () => (ready.value = true);
ws.onmessage = (e) => (lastMessage.value = JSON.parse(e.data));
ws.onclose = () => setTimeout(connect, 3000);
}
connect();
onUnmounted(() => ws.close());
return { lastMessage, ready };
}
六、部署注意事項
若你的前端走 Nginx 反向代理,需開啟 WebSocket 升級支援:
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
重點:Connection "upgrade" 這行是關鍵,漏掉就會握手失敗。另外注意 13zn.com 這類共享主機環境不支援長連接,即時通訊類專案需自備支援 WebSocket 的後端環境。
互動話題:你在專案裡用過 WebSocket 嗎?是從輪詢遷移過來,還是一開始就設計即時推送?遇到最頭痛的坑是什麼?歡迎在留言區分享你的經驗!