如何在MERN网站实现单通道通知及通知铃铛动态计数器
MERN栈实现通知铃铛未读计数器及清零功能
1. 后端(Node.js + Express + MongoDB)
数据模型调整
给通知模型新增isRead字段(默认未读),同时关联用户ID,确保每个用户的通知独立统计:
// models/Notification.js const mongoose = require('mongoose'); const notificationSchema = new mongoose.Schema({ content: { type: String, required: true }, userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, isRead: { type: Boolean, default: false }, createdAt: { type: Date, default: Date.now } }); module.exports = mongoose.model('Notification', notificationSchema);
核心接口实现
- 获取未读通知数量:查询当前用户的未读通知总数
// routes/notifications.js router.get('/unread-count', async (req, res) => { try { // 假设已通过JWT中间件获取当前用户ID并存入req.user const unreadCount = await Notification.countDocuments({ userId: req.user._id, isRead: false }); res.json({ count: unreadCount }); } catch (err) { res.status(500).json({ message: '获取未读数量失败' }); } });
- 标记所有通知为已读:点击铃铛时调用,批量更新当前用户的未读状态
router.put('/mark-all-read', async (req, res) => { try { await Notification.updateMany( { userId: req.user._id, isRead: false }, { $set: { isRead: true } } ); res.json({ message: '所有通知已标记为已读' }); } catch (err) { res.status(500).json({ message: '标记已读失败' }); } });
2. 前端(React)
状态管理与交互逻辑
用React内置状态管理计数器,结合接口实现数据同步:
import { useState, useEffect } from 'react'; function NotificationBell() { const [unreadCount, setUnreadCount] = useState(0); const token = localStorage.getItem('authToken'); // 初始化+定时刷新未读数量 useEffect(() => { fetchUnreadCount(); // 30秒轮询一次,可根据需求调整频率 const interval = setInterval(fetchUnreadCount, 30000); return () => clearInterval(interval); }, []); // 请求未读数量 const fetchUnreadCount = async () => { try { const res = await fetch('/api/notifications/unread-count', { headers: { Authorization: `Bearer ${token}` } }); const data = await res.json(); setUnreadCount(data.count); } catch (err) { console.error('获取未读数量出错:', err); } }; // 点击铃铛触发标记已读+清零计数器 const handleBellClick = async () => { try { await fetch('/api/notifications/mark-all-read', { method: 'PUT', headers: { Authorization: `Bearer ${token}` } }); setUnreadCount(0); // 此处可同步更新通知列表的已读状态 } catch (err) { console.error('标记已读出错:', err); } }; return ( <div onClick={handleBellClick} style={{ cursor: 'pointer', position: 'relative' }}> {/* 替换为你的铃铛图标 */} <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> <path d="M13.73 21a2 2 0 0 1-3.46 0"/> </svg> {/* 未读数量标记,仅当数量>0时显示 */} {unreadCount > 0 && ( <span style={{ position: 'absolute', top: '-4px', right: '-4px', backgroundColor: '#ff4444', color: 'white', borderRadius: '50%', width: '20px', height: '20px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', fontWeight: 'bold' }}> {unreadCount} </span> )} </div> ); } export default NotificationBell;
实时更新优化(可选)
如果需要管理员发布通知后用户立即收到提醒,可替换轮询为Socket.io:
- 后端安装
socket.io,在发布通知时向目标用户推送事件:
// 管理员发布通知的接口中 const newNotification = await Notification.create({ content: req.body.content, userId: req.body.targetUserId }); // 向目标用户的专属房间推送事件 io.to(`user-${req.body.targetUserId}`).emit('new-notification');
- 前端连接Socket.io,收到事件后刷新计数器:
import { io } from 'socket.io-client'; // 在组件的useEffect中添加 useEffect(() => { const socket = io(); // 加入当前用户的专属房间 socket.emit('join-room', `user-${currentUserId}`); // 收到新通知时刷新数量 socket.on('new-notification', fetchUnreadCount); return () => socket.disconnect(); }, [currentUserId]);
关键注意事项
- 确保用户认证逻辑正确,接口能通过
req.user获取当前用户ID - 多用户场景下,严格通过
userId过滤通知,避免数据混淆 - 计数器样式可根据项目UI规范调整,上述为基础示例
内容的提问来源于stack exchange,提问作者Md Hridoy Ahmmed
相关产品推荐
相关产品推荐

