如何使用Socket.IO为MERN栈聊天应用添加消息通知系统
MERN栈聊天应用实时通知系统实现指南
1 数据库层适配
你需要在现有数据库基础上新增notifications集合,字段定义如下:
receiverId: ObjectId,关联用户集合,标记通知接收方senderId: ObjectId,关联用户集合,标记消息发送方chatId: ObjectId,关联会话集合,对应消息所属会话content: String,通知预览内容,可截取消息前20个字符isRead: Boolean,默认值false,标记通知是否已读createdAt: Date,默认值Date.now,记录通知生成时间
同时可以给chats会话集合新增unreadCount字段,类型为Map,键为用户ID,值为对应用户在该会话的未读消息数,方便快速统计全局未读总量。不要在前端单独维护未读计数,所有计数变更以后端返回的为准,避免前后端状态不一致
2 后端逻辑实现
2.1 通知生成逻辑
在现有发送消息的接口逻辑中,新增以下步骤:
- 消息成功存入
messages集合后,向notifications集合插入一条新通知记录 - 对应会话中接收方的
unreadCount自增1 - 通过Socket向接收方的专属房间(通常用用户ID作为房间标识)推送
new-notification事件,携带完整通知数据
示例代码:
// 消息发送后推送通知示例 const newNotification = await Notification.create({ receiverId: receiver._id, senderId: sender._id, chatId: chat._id, content: message.content.slice(0,20) }) // 给接收方推送实时通知 io.to(receiver._id.toString()).emit("new-notification", newNotification)
2.2 配套接口开发
新增两个基础接口即可满足需求:
GET /api/notifications/:userId:返回当前用户所有isRead为false的通知,以及全局未读总数,用于用户登录后初始化通知状态PUT /api/notifications/mark-read:接收userId和chatId两个参数,将该用户对应会话下的所有通知isRead设为true,同时将对应会话的该用户unreadCount清零
3 前端逻辑实现
3.1 全局通知监听
在应用根组件(如App.jsx)挂载时完成以下逻辑:
- 申请浏览器通知权限:调用
Notification.requestPermission(),用户授权后可弹系统级通知 - 监听Socket的
new-notification事件:收到事件后,若用户当前未打开对应会话页面,触发系统通知弹窗,同时更新全局状态(如Redux/Context中的未读通知数,导航栏显示小红点) - 用户登录后第一时间调用
GET /api/notifications/:userId接口,初始化未读通知状态,避免刷新页面后通知丢失
3.2 通知清除逻辑
当用户点击进入对应会话页面时,立即触发以下操作:
- 调用
PUT /api/notifications/mark-read接口,标记该会话所有通知为已读 - 更新全局状态,移除该会话对应的未读通知,同步减少全局未读计数
示例代码:
// 进入会话页标记已读逻辑 const handleEnterChat = async (chatId) => { await axios.put("/api/notifications/mark-read", { userId: currentUser._id, chatId }) // 同步更新全局通知状态 dispatch(clearChatUnread(chatId)) }
4 边界场景处理
- 离线用户通知:如果推送时用户不在线,下次用户登录拉取未读通知时会一次性拿到所有漏收的通知,不会丢失
- 多端同步:如果用户多端登录,可在标记已读接口调用成功后,推送
notification-read事件给对应用户的所有Socket连接,同步多端的通知状态 - 权限拒绝:如果用户拒绝了浏览器通知权限,只保留站内小红点提示即可,不影响核心功能使用
内容的提问来源于stack exchange,提问作者Shehryar Tanoli
相关产品推荐
相关产品推荐

