學習新東西👋#
最近這幾天在學習 React,所以這個專案主要目的是讓我熟悉 React,我個人是喜歡邊學習一樣新東西邊動手,只有輸出才會讓我記憶更深刻,其次也會踩到一些光看是不會碰到的坑,加上之前學習 node.js 好像也沒有做什麼,於是就想到了結合在一塊做一個聊天室。✌
因為是第一次做 React,如果覺得有什麼需要修改的歡迎提出Issuse,也歡迎你的 star⭐
專案地址:ChatRoom
專案截圖:
後端#
後端基於 Node.js + Express + Socket.io + MongoDB
Node.js 操作 MongoDB 以及用 express 寫接口之前都有在博客中大致的總結過:
Node.js+express
Node.Js 操作 MongoDB
這次用的新東西就是 Socket.io。
前人的工作👇#
Socket.IO is a library that enables real-time, bidirectional and event-based communication between the browser and the server
即是說 Socket.io 可以實現伺服器與客戶端之間的一個實時的雙向通訊。
在連接 Socket.io 的之前還得先知道WebSocket,在 WebSocket 問世之前,在創建擁有雙向通訊機制的 web 應用程序時,就只能利用 HTTP 輪詢的方式,由此產生了 “短輪詢” 和 “長輪詢”。
短輪詢通過客戶端定期輪詢來詢問服務端是否有新的信息產生,缺點也是顯而易見,輪詢間隔大了則信息不夠實時,輪詢間隔過小又會消耗過多的流量,增加伺服器的負擔。
長輪詢是對短輪詢的優化,需要服務端做相應的修改來支持。但是每次請求還是都要帶上 HTTP 請求頭部,而且在長輪詢的連接結束之後,伺服器端積累的新消息要等到下次客戶端連接時才能傳遞。
Websocket 協議就是為了解決長輪詢的痛點而誕生的,其基於 TCP 協議,是一種雙全工通訊技術、複用 HTTP 握手通道。它與 HTTP 協議的唯一關係就是它的握手請求可以作為一個Upgrade request經由 HTTP 伺服器解析,且與 HTTP 使用一樣的端口。
接著是Socket.io,Socket.io 底層基於engine.io,封裝了 WebSocket,其屏蔽了底層細節,讓頂層調用非常簡單。同時它還支持許多種輪詢機制以及其他通訊方式,當環境不支持 WebSocket 的時候它能自動選擇最佳的方式來實現網路的實時通訊。
使用#
引入 io 設置端口後監聽 connect 事件:
const server = require('http').Server(app);
const io = require('socket.io')(server);
server.listen(3001); //端口設置3001
io.on(('connection', socket=>{
………………
})
接著通過最重要的兩個 api,emit和on來發送以及監聽事件
- socket.emit (eventName,[ ...args]):發射(觸發)一個事件
- socket.on (eventName, callback):監聽一個 emit 發射的事件
//監聽xxx事件,輸出傳進來的data對象
socket.on('xxx',data=>{
console.log(data);
})
//發送xxx事件,傳出去一個對象,裡面有name屬性
socket.emit('xxx',{name:'magren'})
另外用到的方法
- socket.join (id):加入到一個 room 為 id 的房間中
- socket.broadcast.to (id).emit ( ):廣播給 room 為 id 的除了‘我’以外的所有人
- io.sockets.in (id).emit ( ):廣播給 room 為 id 的所有人
前端#
前端基於 React + Redux + Typescript + Antd
向後臺發送消息以及監聽很簡單,只需要引入socket.io-client,接著使用 emit 發送和 on 監聽事件即可。
const socket = require('socket.io-client')('ws://localhost:3001',{transports: ['websocket']})
//監聽信息
socket.on('chat_message',(data: mesItem)=>{
this.setState({
message:[data,...this.state.message]
});
})
//發送加入群組
socket.emit('join', {
roomId:this.props.match.params.roomId,
userName:this.props.state.name,
userId:this.props.state.id
})
//發送信息
socket.emit('mes',{
roomId:this.props.match.params.roomId,
userName:this.props.state.name,
userId:this.props.state.id,
mes:this.state.msg
})
關於 Redux#
redux 跟 vuex 的理念給我的感覺是一樣的,同樣都是一個狀態管理庫,並且都是保存在內存當中(刷新就會重置),定義全局 state,觸發後修改 state。不同的地方也還是有的,Vuex 的數據是可變可直接修改,Redux 不可變並且是直接用新的 state 替換掉舊的 state。
這個專案用了 react-redux 和 redux-thunk 兩個庫。
使用了 react-redux 後流程大大縮短,store 的三大功能:dispatch,subscribe,getState 都讓它來幫我們實現了,同時它提供了 Provider 和 Connect,前者是一個組件,後者是一個函數。
大致的流程就是 Provider 組件接受 redux 的 store 作為 props,然後通過 context 往下傳,connect 函數收到 Provider 傳出的 store,並將 state 和 actionCreator 以 props 傳入組件,組件就可以通過調用 props 裡的 action 觸發 reducer 函數返回新的 state,connect 監聽到變化後調用 setState 更新組件並將新的 state 傳入。
redux-thunk的作用:可以讓 store.dispatch 變成可以接收一個函數 / 一個對象的中間件。
讓原本只能接受對象的 store.dispatch 變成可以接收對象 / 方法,並且如果接收了一個方法後自動執行該方法,而不觸發 redux 的 store 更新。
最後💻#
感謝 Google、baidu 等搜索引擎和 Google 翻譯😵
以及bailicangdu大佬的react-pxq專案的參考以及總結😘