一、概述
Websocket 是 H5 自帶的一個 API,隨著越來越多的瀏覽器都自適應(yīng)了 H5 的特性,許多瀏覽器也內(nèi)置了 WebSocket API。也就是說 WebSocket 和 window、document 一樣作為全局變量可以直接使用。
要在瀏覽器端使用 WebSocket,首先需要服務(wù)端支持 WebSocket,假設(shè)現(xiàn)在服務(wù)端已提供 WebSocket 服務(wù),訪問地址:ws://localhost:8080,簡單介紹下瀏覽器使用方法。
// 連接 Websocket 服務(wù)端
const ws = new WebSocket("ws://localhost:8080");
// 監(jiān)聽連接上 Websocket 服務(wù)端觸發(fā)事件
ws.onopen = function (e) {
console.log('連接上 ws 服務(wù)端了');
// ws.send() 給服務(wù)端發(fā)送數(shù)據(jù)
ws.send('我是客戶端,我接收到你的請求了');
}
// 監(jiān)聽 Websocket 服務(wù)端傳來消息觸發(fā)事件
ws.onmessage = function(msg) {
// msg.data 接收服務(wù)端傳遞過來的數(shù)據(jù)
console.log('接收服務(wù)端發(fā)過來的消息: %o', msg.data);
};
// 監(jiān)聽 Websocket 服務(wù)端連接斷開觸發(fā)事件
ws.onclose = function (e) {
console.log('ws 連接關(guān)閉了');
}
注意事項(xiàng): WebSocket 通信傳遞的數(shù)據(jù)是字符串,即便瀏覽器端傳給服務(wù)端的是個對象,在服務(wù)端接收時也會變成字符串,可以通過 JSON.parse(msg.data) 解析成對象。
二、dva 中使用 WebSocket
下面是我寫小說爬蟲用到的部分代碼,點(diǎn)擊爬取,在瀏覽器端打印服務(wù)端爬蟲日志。

dva 是基于 React 的狀態(tài)管理器,不能直接對 Dom 進(jìn)行操作。要持續(xù)不斷的接收服務(wù)端響應(yīng)數(shù)據(jù),需要在構(gòu)造器中定義一個 state 屬性進(jìn)行接收。直接在 routes 目錄下的頁面中連接服務(wù)端 WebSocket 并調(diào)用 API 接口。
(用箭頭函數(shù)寫組件的寫法是沒有 state 特性的,這一點(diǎn)在 React 官方文檔中有詳細(xì)說明,具體可參考 React 開發(fā)中不得不注意的兩個大坑)
// src/routes/novel/index.js
class NovelComp {
constructor (props) {
super(props);
this.state = { result: '' }; // 接收 Websocket 響應(yīng)數(shù)據(jù)
}
handleSteal (flag) {
const ws = new WebSocket('ws://localhost:8080');
let result = this.state.result;
ws.onopen = function (e) {
console.log('連接上 ws 服務(wù)端了');
ws.send(JSON.stringify({ flag: flag, data: currentItem }));
}
ws.onmessage = function(msg) {
console.log('接收服務(wù)端發(fā)過來的消息: %o', msg);
result += msg.data + '\n';
that.setState({ result: result });
};
ws.onclose = function (e) {
console.log('ws 連接關(guān)閉了');
}
}
render () {
return (
<Modal>
<Button></Button>
....
<Modal/>
);
}
}
三、一直連接 WebSocket
上面的 WebSocket 應(yīng)用場景是點(diǎn)擊按鈕才會連接 WebSocket,關(guān)閉模態(tài)框 WebSocket 連接即斷開。
有些需求需要 WebSocket 一直連接,比如 待辦事項(xiàng)主動提醒。如果其它操作新增了一條通知,當(dāng)前用戶的通知條目應(yīng)當(dāng)自動變成 6,而不是下一次刷新完再更新。

由于 dva 本身的特性,如果刷新頁面,dva 所有狀態(tài)容器中的值都會清空,與此同時 WebSocket 連接也會斷開,也就沒法監(jiān)聽服務(wù)端傳過來的數(shù)據(jù)。
解決方案: 修改 src/models/novel.js 的 subscriptions 屬性。
subscriptions: {
setupHistory ({ dispatch, history }) {
history.listen((location) => {
if (location.pathname.indexOf('reptile/novel')) {
const ws = new WebSocket('ws://localhost:8080');
dispatch({
type: 'updateState',
payload: { ws: ws },
});
}
})
},
},
subscriptions 下面 setupHistory 的作用是監(jiān)聽瀏覽器地址欄地址如果變成當(dāng)前頁面 reptile/novel 就創(chuàng)建 WebSocket 連接,這樣就能保證每次進(jìn)入這個頁面的時候都已連接 WebSocket。
對于 待辦事項(xiàng)主動提醒 那個需求,只需在布局文件中的 subscriptions 中添加上述代碼即可。