学习新东西👋#
最近这几天在学习 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项目的参考以及总结😘