You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现Vue.js离线用户上线后接收Notification通知(Socket.io/Node.js)

嗨,你已经把实时通知的基础框架搭得很不错了!要实现离线用户上线后自动收到历史通知,核心就是把通知持久化存起来,等用户上线时针对性地把他们没收到的通知推过去。我给你拆解一下具体的实现步骤和代码修改建议:

核心思路
  1. 给Socket连接绑定用户身份:现在你的代码没有区分用户,得让每个连接都带上用户的唯一标识(比如用户ID),这样后端才知道该给谁推历史通知。
  2. 持久化存储通知:把所有发送的通知存到数据库里(别用内存存,服务器重启就没了),方便后续查询。
  3. 上线拉取未读通知:用户上线时,后端查询该用户的未读通知,批量推送给前端。
  4. 标记已读状态:推送后把这些通知标记为已读,避免重复推送。
具体实现代码

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

火山引擎 最新活动