如何实现Vue.js离线用户上线后接收Notification通知(Socket.io/Node.js)
嗨,你已经把实时通知的基础框架搭得很不错了!要实现离线用户上线后自动收到历史通知,核心就是把通知持久化存起来,等用户上线时针对性地把他们没收到的通知推过去。我给你拆解一下具体的实现步骤和代码修改建议:
核心思路
- 给Socket连接绑定用户身份:现在你的代码没有区分用户,得让每个连接都带上用户的唯一标识(比如用户ID),这样后端才知道该给谁推历史通知。
- 持久化存储通知:把所有发送的通知存到数据库里(别用内存存,服务器重启就没了),方便后续查询。
- 上线拉取未读通知:用户上线时,后端查询该用户的未读通知,批量推送给前端。
- 标记已读状态:推送后把这些通知标记为已读,避免重复推送。
具体实现代码
1. 后端(Node.js + Socket.io + MongoDB)
首先我们用MongoDB来存通知,先安装依赖:
npm install mongoose
然后修改你的后端代码:
let express = require('express'); let app = express(); let http = require('http').createServer(app); let io = require('socket.io')(http); const mongoose = require('mongoose'); // 连接你的MongoDB数据库(替换成自己的地址) mongoose.connect('mongodb://localhost:27017/notificationDB') .then(() => console.log('✅ MongoDB连接成功')) .catch(err => console.error('❌ MongoDB连接失败:', err)); // 定义通知的数据模型 const NotificationSchema = new mongoose.Schema({ title: String, text: String, createdAt: { type: Date, default: Date.now }, readBy: [{ type: String }] // 存储已读此通知的用户ID }); const Notification = mongoose.model('Notification', NotificationSchema); io.on('connection', async (socket) => { // 从Socket连接的查询参数里获取用户ID(前端传过来的) const userId = socket.handshake.query.userId; if (!userId) { console.log('⚠️ 用户未提供身份标识,断开连接'); socket.disconnect(); return; } console.log(`👤 用户 ${userId} 上线了`); // 📥 拉取该用户的未读通知 const unreadNotifications = await Notification.find({ readBy: { $nin: [userId] } // 筛选出该用户没读过的通知 }); // 如果有未读通知,批量推送给当前用户 if (unreadNotifications.length > 0) { socket.emit('sendNotificationBatch', unreadNotifications); // 标记这些通知为已读 await Notification.updateMany( { _id: { $in: unreadNotifications.map(n => n._id) } }, { $addToSet: { readBy: userId } } ); } // 📤 处理发送新通知的逻辑 socket.on('sendingNotification', async (bodyMessage) => { console.log('收到新通知:', bodyMessage); // 先把通知存到数据库 const newNotification = new Notification({ title: bodyMessage.title, text: bodyMessage.text }); await newNotification.save(); // 推送给所有在线用户 io.emit('sendNotification', bodyMessage); }); socket.on('disconnect', () => { console.log(`👤 用户 ${userId} 下线了`); }); }); http.listen(2000, () => { console.log('🚀 服务器运行在 http://localhost:2000'); });
2. 前端(Vue.js)
修改你的Vue组件,连接Socket时带上用户ID(假设用户登录后ID存在localStorage里):
export default{ data(){ return{ title:"", text:"", userId: localStorage.getItem('userId') || '', // 从本地存储取用户ID socket: null, } }, mounted(){ // 初始化Socket连接,带上用户ID this.socket = io("http://localhost:2000", { query: { userId: this.userId } }); // 接收实时推送的单条通知 this.socket.on("sendNotification", body => { new Notification(body.title, { body: body.text, icon: '/your-icon.png' // 可选:加个通知图标 }); }); // 接收上线时推送的批量未读通知 this.socket.on("sendNotificationBatch", notifications => { notifications.forEach(notification => { new Notification(notification.title, { body: notification.text, icon: '/your-icon.png' }); }); }); }, methods:{ sendNotification(){ if (!this.title || !this.text) { alert('请填写通知标题和内容!'); return; } this.socket.emit("sendingNotification", { title: this.title, text: this.text }); // 发送后清空表单 this.title = ""; this.text = ""; } } }
表单部分保持你原来的代码就行,没问题。
重要注意事项
- 用户身份验证:上面的示例直接用前端传的userId,实际生产环境一定要加验证!比如用JWT令牌,前端把token传给Socket,后端解析token得到真实的userId,防止伪造用户身份。
- 存储方案选择:如果不想用MongoDB,也可以用Redis(适合高频读写场景)或者MySQL,核心就是要持久化存储通知。
- 已读状态优化:如果用户量很大,用
readBy数组查询可能会慢,可以改成给每个用户维护一个「最后读取通知的时间戳」,上线时只拉取时间戳之后的通知,效率更高。
这样改造后,离线用户下次上线时,就能收到他们离线期间发送的所有通知啦!
内容的提问来源于stack exchange,提问作者Ashkan




