Node.js provides an efficient server-side workbench, but because the browser side has different HTML5 support to compare browsers, provide a great real-time user experience, and provide programmers with client-server consistency. programming expertise, hence Socket.io was born. Socket.io encapsulates WebSocket, polling mechanism and other real-time communication methods into public interfaces, and executes the code corresponding to these real-time mechanisms on the server.
Socket.io implements communication mechanisms based on real events. The goal is to allow different browsers and mobile devices to implement the actual features of the applications and obliterate the different transmission mechanisms. Socket.io is a cross platform. A variety of connection methods are automatically switched. IM development is convenient and can be well combined with the traditional request method provided by ExpressJS.
Code running environment
end of introduction | end of the road | Database |
---|---|---|
react | pass | MongoDB |
basic dependencies
"express": "^4.17.1",
"socket.io": "^3.1.0",
"socket.io-client": "^3.1.0",
end of the road
(The chat room is started by the user, so when creating the chat form, the user form must also be set)
1. server. js
(project entry file, Socket.io configuration)
const express = require('express')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const model = require('./model')
const Chat = model.getModel('chat')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server, { cors: true })
io.on('connection', function(socket) {
socket.on('sendmsg', function(data) {
const { from, to, msg } = data
const chatid = [from, to].sort().join('_')
Chat.create({chatid, from, to, content: msg}, function(err, doc) {
io.emit('recvmsg', Object.assign({}, doc._doc))
})
})
})
const userRouter = require('./user')
app.use(cookieParser())
app.use(bodyParser.json())
app.use('/user', userRouter)
server.listen(9093, function() {
console.log('server port is 9093')
})
2.model.js
const mongoose = require('mongoose')
const DB_URL = 'mongodb://localhost:27017/goodwork'
mongoose.connect(DB_URL)
const models = {
user:{
'user':{type:String, 'require':true},
'pwd':{type:String, 'require':true},
'type':{'type':String, 'require':true},
'avatar':{'type':String},
'desc':{'type':String},
'title':{'type':String},
'company':{'type':String},
'money':{'type':String}
},
chat:{
'chatid': {type: String, require: true},
'from': {type: String, require: true},
'to': {type: String, require: true},
'read': {type: Boolean, default: false},
'content': {type: String, require: true, deafult: ''},
'create_time': {type: Number, deafult: new Date().getTime()}
}
}
for(let m in models){
mongoose.model(m, new mongoose.Schema(models[m]))
}
module.exports = {
getModel:function(name){
return mongoose.model(name)
}
}
3. user. js
(routing files, writing message lists and unread lists)
const { json } = require('body-parser')
const express = require('express')
const utils = require('utility')
const Router = express.Router()
const model = require('./model')
const User = model.getModel('user')
const Chat = model.getModel('chat')
const _filter = { 'pwd': 0, '__v': 0 }
Router.get('/getmsglist', function(req, res) {
const user = req.cookies.userid
User.find({}, function(e, userdoc) {
let users = {}
userdoc.forEach(v => {
users[v._id] = { name: v.user, avatar: v.avatar }
})
Chat.find({'$or': [{ from: user}, { to: user }]}, function(err, doc) {
if (!err) {
return res.json({
code: 0,
msgs: doc,
users: users
})
}
})
})
})
Router.post('/readmsg', function (req, res) {
const userid = req.cookies.userid
const { from } = req.body
console.log(userid, from)
Chat.updateMany(
{ from, to: userid },
{ '$set': { read: true } },
{ 'multi': true },
function (err, doc) {
console.log(doc)
if (!err) {
return res.json({
code: 0,
num: doc.nModified
})
}
return res.json({
code: 1,
msg: 'ошибка'
})
})
})
module.exports = Router
end of introduction
(Writing in front is more complicated, it involves jumping between each page and processing data between Redux. I can’t describe all the details clearly here, please forgive me)
The front-end page uses Redux to manage the data
1. redux/chat.redux.js
import axios from 'axios'
import io from 'socket.io-client'
const socket = io('ws://localhost:9093')
const MSG_LIST = 'MSG_LIST'
const MSG_RECV = 'MSG_RECV'
const MSG_READ = 'MSG_READ'
const initState = {
chatmsg: [],
users: {},
unread: 0
}
export function chat(state = initState, action) {
switch (action.type) {
case MSG_LIST:
return { ...state,
users: action.payload.users,
chatmsg: action.payload.msgs,
unread: action.payload.msgs.filter(v => !v.read && v.to === action.payload.userid).length
}
case MSG_RECV:
const n = action.payload.to === action.userid ? 1 : 0
return {
...state,
chatmsg: [...state.chatmsg, action.payload],
unread: state.unread + n
}
case MSG_READ:
const { from } = action.payload
return {
...state,
chatmsg: state.chatmsg.map(v => ({...v, read: from === v.from ? true : v.read})),
unread: state.unread.num ? state.unread.num : 0}
default:
return state
}
}
function msgRecv(msg, userid) {
return { userid, type: MSG_RECV, payload: msg }
}
export function recvMsg() {
return (dispatch, getState) => {
socket.on('recvmsg', function(data) {
const userid = getState().user._id
dispatch(msgRecv(data, userid))
})
}
}
function msgList(msgs, users, userid) {
return { type: MSG_LIST, payload: {msgs, users, userid} }
}
export function getMsgList() {
return (dispatch, getState) => {
axios.get('/user/getmsglist').then(res => {
console.log(res)
if (res.status === 200 && res.data.code === 0) {
const userid = getState().user._id
dispatch(msgList(res.data.msgs, res.data.users, userid))
}
})
}
}
export function sendMsg({ from, to, msg }) {
return dispatch => {
socket.emit('sendmsg', { from, to, msg })
}
}
function msgRead({ from, userid, num }) {
return { type: MSG_READ, payload: { from, userid, num } }
}
export function readMsg(from) {
return (dispatch, getState) => {
axios.post('/user/readmsg', {from}).then(res => {
const userid = getState().user._id
if (res.status === 200 && res.data.code === 0) {
const num = res.data.num
dispatch(msgRead({ userid, from, num }))
}
})
}
}
2. chat. js
import { Grid, Icon, InputItem, List, NavBar } from 'antd-mobile';
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { getMsgList, sendMsg, recvMsg, readMsg } from '../../redux/chat.redux'
import { getChatId } from '../../util'
@connect(
state => state,
{ getMsgList, sendMsg, recvMsg, readMsg }
)
class Chat extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
msg: []
}
}
componentDidMount() {
if (!this.props.chat.chatmsg.length) {
this.props.getMsgList()
this.props.recvMsg()
}
}
componentWillUnmount() {
const to = this.props.match.params.user
this.props.readMsg(to)
}
fixCarousel() {
setTimeout(function () {
window.dispatchEvent(new Event('resize'))
}, 0)
}
handleSubmit() {
const from = this.props.user._id
const to = this.props.match.params.user
const msg = this.state.text
this.props.sendMsg({ from, to, msg })
this.setState({
text: '',
showEmoji: false
})
}
render() {
const emoji = '😀 😁 😂 😍 😘 😝 😬 🤮 🤟 🤏 👌 🤜 🙏'
.split(' ')
.filter(v => v)
.map(v => ({ text: v }))
const userid = this.props.match.params.user
const Item = List.Item
const users = this.props.chat.users
if (!users[userid]) {
return null
}
const chatid = getChatId(userid, this.props.user._id)
const chatmsgs = this.props.chat.chatmsg.filter(v => v.chatid === chatid)
return (
<div id="chat-page">
<div style={{ position: "fixed", zIndex: 999, width: '100%' }}>
<NavBar
mode="dark"
icon={<Icon type="left"></Icon>}
onLeftClick={() => {
this.props.history.goBack()
}}
>
{users[userid].name}
</NavBar>
</div>
{}
{chatmsgs.map(v => {
const avatar = require(`../img/${users[v.from].avatar}.png`).default
return v.from === userid ? (
<List key={v._id}>
<Item
thumb={avatar}
multipleLine={true}
wrap={true}
platform="android"
>{v.content}</Item>
</List>
) : (
<List key={v._id}>
<Item
multipleLine={true}
wrap={true}
platform="android"
extra={<img src={avatar} alt="" />
}
className="chat-me"
>{v.content}</Item>
</List>
)
})}
{}
<div className="stick-fooetr">
<List>
<InputItem
placeholder='Пожалуйста, входите'
value={this.state.text}
onChange={v => {
this.setState({ text: v })
}}
extra={
<div>
<span
style={{ marginRight: 15 }}
onClick={() => {
this.setState({
showEmoji: !this.state.showEmoji
})
this.fixCarousel()
}}
>😀</span>
<span onClick={() => this.handleSubmit()}>Отправить</span>
</div>
}
></InputItem>
{this.state.showEmoji ? <Grid
data={emoji}
columnNum={9}
carouselMaxRow={2}
isCarousel={true}
onClick={el => {
this.setState({
text: this.state.text + el.text
})
console.log(el)
}}
/> : null}
</List>
</div>
</div>
)
}
}
export default Chat
The concrete code can access the authority of gitee
You can also look at the author’s public account: Daming Lady, (Answer: Socket.io) Get tutorials